use quote::ToTokens; use super::util::*; use crate::{ generate::{convert_to_owned_type::convert_to_owned_type, snippets, supertrait_gen}, model::{ArgPaymentMetadata, ContractTrait, Method, MethodArgument, PublicRole}, }; pub fn proxy_arg_gen( method_args: &[MethodArgument], generics: &mut syn::Generics, ) -> Vec { let mut args_decl = Vec::new(); for (arg_index, arg) in method_args.iter().enumerate() { let unprocessed_attributes = &arg.unprocessed_attributes; let pat = &arg.pat; if arg.is_endpoint_arg() { // let ty = &arg.ty; let mut bounds = syn::punctuated::Punctuated::new(); bounds.push(syn::TypeParamBound::Trait(syn::TraitBound { paren_token: None, modifier: syn::TraitBoundModifier::None, lifetimes: None, path: equivalent_encode_path_gen(&arg.ty), })); let arg_type_generated_ident = generate_proxy_type_generic(arg_index); generics .params .push(syn::GenericParam::Type(syn::TypeParam { attrs: Vec::new(), ident: arg_type_generated_ident.clone(), colon_token: Some(syn::token::Colon(proc_macro2::Span::call_site())), bounds, eq_token: None, default: None, })); args_decl.push(quote! { #(#unprocessed_attributes)* #pat : #arg_type_generated_ident }); } else { let ty = &arg.ty; args_decl.push(quote! { #(#unprocessed_attributes)* #pat : #ty }); } } args_decl } pub fn generate_proxy_method_sig( method: &Method, return_type: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { let method_name = &method.name; let mut generics = method.generics.clone(); let generics_where = &method.generics.where_clause; let arg_decl = proxy_arg_gen(&method.method_args, &mut generics); let result = quote! { fn #method_name #generics ( &mut self, #(#arg_decl),* ) -> #return_type #generics_where }; result } fn original_type_tokens(m: &Method) -> proc_macro2::TokenStream { match &m.return_type { syn::ReturnType::Default => quote! { () }, syn::ReturnType::Type(_, ty) => quote! { #ty }, } } pub fn generate_proxy_endpoint(m: &Method, endpoint_name: String) -> proc_macro2::TokenStream { let mut token_count = 0; let mut token_expr = quote! { drt_sc::types::RewaOrDcdtTokenIdentifier::::rewa() }; let mut nonce_count = 0; let mut nonce_expr = quote! { 0u64 }; let mut payment_count = 0; let mut payment_expr = quote! { drt_sc::types::BigUint::::zero() }; let mut multi_count = 0; let mut multi_expr_opt = None; let mut arg_push_snippets = Vec::::new(); for arg in &m.method_args { match &arg.metadata.payment { ArgPaymentMetadata::NotPayment => { let pat = &arg.pat; arg_push_snippets.push(quote! { .argument(&#pat) }); }, ArgPaymentMetadata::PaymentToken => { token_count += 1; let pat = &arg.pat; token_expr = quote! { #pat }; }, ArgPaymentMetadata::PaymentNonce => { nonce_count += 1; let pat = &arg.pat; nonce_expr = quote! { #pat }; }, ArgPaymentMetadata::PaymentAmount => { payment_count += 1; let pat = &arg.pat; payment_expr = quote! { #pat }; }, ArgPaymentMetadata::PaymentMulti => { multi_count += 1; let pat = &arg.pat; multi_expr_opt = Some(quote! { #pat }); }, } } assert!( payment_count <= 1, "No more than one payment argument allowed in call proxy" ); assert!( token_count <= 1, "No more than one payment token argument allowed in call proxy" ); assert!( nonce_count <= 1, "No more than one payment nonce argument allowed in call proxy" ); assert!( multi_count <= 1, "No more than one payment multi argument allowed in call proxy" ); let payment_type; let payment_init; if token_count > 0 || nonce_count > 0 || payment_count > 0 { assert!(multi_count == 0, "#[payment_multi] cannot coexist with any other payment annotation in the same endpoint"); if token_count == 0 && nonce_count == 0 { payment_type = quote! { drt_sc::types::RewaPayment }; payment_init = quote! { .rewa(#payment_expr) }; } else { payment_type = quote! { drt_sc::types::RewaOrDcdtTokenPayment }; payment_init = quote! { .payment( drt_sc::types::RewaOrDcdtTokenPayment::new( #token_expr, #nonce_expr, #payment_expr, ) )}; } } else if multi_count > 0 { let multi_expr = multi_expr_opt.unwrap(); payment_type = quote! { MultiDcdtPayment }; payment_init = quote! { .multi_dcdt(#multi_expr.clone_value()) }; } else { payment_type = quote! { () }; payment_init = quote! {}; } let original_type = original_type_tokens(m); let return_type = quote! { drt_sc::types::Tx< drt_sc::types::TxScEnv, (), Self::To, #payment_type, (), drt_sc::types::FunctionCall, drt_sc::types::OriginalResultMarker<#original_type>, > }; let msig = generate_proxy_method_sig(m, return_type); let sig = quote! { #[allow(clippy::too_many_arguments)] #[allow(clippy::type_complexity)] #msig { drt_sc::types::TxBaseWithEnv::new_tx_from_sc() .to(self.extract_proxy_to()) .original_result() .raw_call(#endpoint_name) #payment_init #(#arg_push_snippets)* } }; sig } pub fn generate_proxy_deploy(init_method: &Method) -> proc_macro2::TokenStream { let mut payment_count = 0; let mut multi_count = 0; let mut token_count = 0; let mut nonce_count = 0; let mut payment_type = quote! { () }; let mut payment_init = quote! {}; let mut arg_push_snippets = Vec::::new(); for arg in &init_method.method_args { match &arg.metadata.payment { ArgPaymentMetadata::NotPayment => { let pat = &arg.pat; arg_push_snippets.push(quote! { .argument(&#pat) }); }, ArgPaymentMetadata::PaymentToken => { token_count += 1; }, ArgPaymentMetadata::PaymentNonce => { nonce_count += 1; }, ArgPaymentMetadata::PaymentAmount => { payment_count += 1; let payment_expr = &arg.pat; payment_type = quote! { drt_sc::types::RewaPayment }; payment_init = quote! { .rewa(#payment_expr) }; }, ArgPaymentMetadata::PaymentMulti => { multi_count += 1; }, } } assert!( payment_count <= 1, "No more than one payment argument allowed in call proxy" ); assert!(token_count == 0, "No DCDT payment allowed in #[init]"); assert!(nonce_count == 0, "No SFT/NFT payment allowed in #[init]"); assert!( multi_count == 0, "No multi DCDT payments allowed in #[init]" ); let original_type = original_type_tokens(init_method); let return_type = quote! { drt_sc::types::Tx< drt_sc::types::TxScEnv, (), Self::To, // still accepted, until we separate the upgrade constructor completely #payment_type, (), drt_sc::types::DeployCall, ()>, drt_sc::types::OriginalResultMarker<#original_type>, > }; let msig = generate_proxy_method_sig(init_method, return_type); let sig = quote! { #[allow(clippy::too_many_arguments)] #[allow(clippy::type_complexity)] #msig { drt_sc::types::TxBaseWithEnv::new_tx_from_sc() .raw_deploy() #payment_init #(#arg_push_snippets)* .original_result() .to(self.extract_proxy_to()) // still accepted, until we separate the upgrade constructor completely } }; sig } pub fn generate_method_impl(contract_trait: &ContractTrait) -> Vec { contract_trait .methods .iter() .filter_map(|m| match &m.public_role { PublicRole::Init(_) => Some(generate_proxy_deploy(m)), PublicRole::Upgrade(_) => Some(generate_proxy_endpoint(m, "upgrade".to_string())), PublicRole::Endpoint(endpoint_metadata) => Some(generate_proxy_endpoint( m, endpoint_metadata.public_name.to_string(), )), _ => None, }) .collect() } pub fn proxy_trait(contract: &ContractTrait) -> proc_macro2::TokenStream { let proxy_supertrait_decl = supertrait_gen::proxy_supertrait_decl(contract.supertraits.as_slice()); let proxy_methods_impl = generate_method_impl(contract); quote! { pub trait ProxyTrait: drt_sc::contract_base::ProxyObjBase + Sized #(#proxy_supertrait_decl)* { #(#proxy_methods_impl)* } } } pub fn proxy_obj_code(contract: &ContractTrait) -> proc_macro2::TokenStream { let proxy_object_def = snippets::proxy_object_def(); let impl_all_proxy_traits = supertrait_gen::impl_all_proxy_traits(contract.supertraits.as_slice()); quote! { #proxy_object_def #(#impl_all_proxy_traits)* } } fn equivalent_encode_path_gen(ty: &syn::Type) -> syn::Path { let owned_type = convert_to_owned_type(ty); syn::parse_str( format!( "drt_sc::types::ProxyArg<{}>", owned_type.to_token_stream() ) .as_str(), ) .unwrap() }