multiversx_sc_derive/generate/
proxy_gen.rs1use quote::ToTokens;
2
3use super::util::*;
4use crate::{
5 generate::{convert_to_owned_type::convert_to_owned_type, snippets, supertrait_gen},
6 model::{ArgPaymentMetadata, ContractTrait, Method, MethodArgument, PublicRole},
7};
8
9pub fn proxy_arg_gen(
10 method_args: &[MethodArgument],
11 generics: &mut syn::Generics,
12) -> Vec<proc_macro2::TokenStream> {
13 let mut args_decl = Vec::new();
14 for (arg_index, arg) in method_args.iter().enumerate() {
15 let unprocessed_attributes = &arg.unprocessed_attributes;
16 let pat = &arg.pat;
17 if arg.is_endpoint_arg() {
18 let mut bounds = syn::punctuated::Punctuated::new();
20 bounds.push(syn::TypeParamBound::Trait(syn::TraitBound {
21 paren_token: None,
22 modifier: syn::TraitBoundModifier::None,
23 lifetimes: None,
24 path: equivalent_encode_path_gen(&arg.ty),
25 }));
26 let arg_type_generated_ident = generate_proxy_type_generic(arg_index);
27 generics
28 .params
29 .push(syn::GenericParam::Type(syn::TypeParam {
30 attrs: Vec::new(),
31 ident: arg_type_generated_ident.clone(),
32 colon_token: Some(syn::token::Colon(proc_macro2::Span::call_site())),
33 bounds,
34 eq_token: None,
35 default: None,
36 }));
37 args_decl.push(quote! { #(#unprocessed_attributes)* #pat : #arg_type_generated_ident });
38 } else {
39 let ty = &arg.ty;
40 args_decl.push(quote! { #(#unprocessed_attributes)* #pat : #ty });
41 }
42 }
43
44 args_decl
45}
46
47pub fn generate_proxy_method_sig(
48 method: &Method,
49 return_type: proc_macro2::TokenStream,
50) -> proc_macro2::TokenStream {
51 let method_name = &method.name;
52 let mut generics = method.generics.clone();
53 let generics_where = &method.generics.where_clause;
54 let arg_decl = proxy_arg_gen(&method.method_args, &mut generics);
55 let result = quote! {
56 fn #method_name #generics (
57 &mut self,
58 #(#arg_decl),*
59 ) -> #return_type
60 #generics_where
61 };
62 result
63}
64
65fn original_type_tokens(m: &Method) -> proc_macro2::TokenStream {
66 match &m.return_type {
67 syn::ReturnType::Default => quote! { () },
68 syn::ReturnType::Type(_, ty) => quote! { #ty },
69 }
70}
71
72pub fn generate_proxy_endpoint(m: &Method, endpoint_name: String) -> proc_macro2::TokenStream {
73 let mut token_count = 0;
74 let mut token_expr =
75 quote! { multiversx_sc::types::EgldOrEsdtTokenIdentifier::<Self::Api>::egld() };
76 let mut nonce_count = 0;
77 let mut nonce_expr = quote! { 0u64 };
78 let mut payment_count = 0;
79 let mut payment_expr = quote! { multiversx_sc::types::BigUint::<Self::Api>::zero() };
80 let mut multi_count = 0;
81 let mut multi_expr_opt = None;
82
83 let mut arg_push_snippets = Vec::<proc_macro2::TokenStream>::new();
84
85 for arg in &m.method_args {
86 match &arg.metadata.payment {
87 ArgPaymentMetadata::NotPayment => {
88 let pat = &arg.pat;
89 arg_push_snippets.push(quote! {
90 .argument(&#pat)
91 });
92 }
93 ArgPaymentMetadata::PaymentToken => {
94 token_count += 1;
95 let pat = &arg.pat;
96 token_expr = quote! { #pat };
97 }
98 ArgPaymentMetadata::PaymentNonce => {
99 nonce_count += 1;
100 let pat = &arg.pat;
101 nonce_expr = quote! { #pat };
102 }
103 ArgPaymentMetadata::PaymentAmount => {
104 payment_count += 1;
105 let pat = &arg.pat;
106 payment_expr = quote! { #pat };
107 }
108 ArgPaymentMetadata::PaymentMulti => {
109 multi_count += 1;
110 let pat = &arg.pat;
111 multi_expr_opt = Some(quote! { #pat });
112 }
113 }
114 }
115
116 assert!(
117 payment_count <= 1,
118 "No more than one payment argument allowed in call proxy"
119 );
120 assert!(
121 token_count <= 1,
122 "No more than one payment token argument allowed in call proxy"
123 );
124 assert!(
125 nonce_count <= 1,
126 "No more than one payment nonce argument allowed in call proxy"
127 );
128 assert!(
129 multi_count <= 1,
130 "No more than one payment multi argument allowed in call proxy"
131 );
132
133 let payment_type;
134 let payment_init;
135 if token_count > 0 || nonce_count > 0 || payment_count > 0 {
136 assert!(multi_count == 0, "#[payment_multi] cannot coexist with any other payment annotation in the same endpoint");
137
138 if token_count == 0 && nonce_count == 0 {
139 payment_type = quote! { multiversx_sc::types::EgldPayment<Self::Api> };
140 payment_init = quote! { .egld(#payment_expr) };
141 } else {
142 payment_type = quote! { multiversx_sc::types::EgldOrEsdtTokenPayment<Self::Api> };
143 payment_init = quote! { .payment(
144 multiversx_sc::types::EgldOrEsdtTokenPayment::new(
145 #token_expr,
146 #nonce_expr,
147 #payment_expr,
148 )
149 )};
150 }
151 } else if multi_count > 0 {
152 let multi_expr = multi_expr_opt.unwrap();
153 payment_type = quote! { MultiEsdtPayment<Self::Api> };
154 payment_init = quote! { .multi_esdt(#multi_expr.clone_value()) };
155 } else {
156 payment_type = quote! { () };
157 payment_init = quote! {};
158 }
159
160 let original_type = original_type_tokens(m);
161 let return_type = quote! {
162 multiversx_sc::types::Tx<
163 multiversx_sc::types::TxScEnv<Self::Api>,
164 (),
165 Self::To,
166 #payment_type,
167 (),
168 multiversx_sc::types::FunctionCall<Self::Api>,
169 multiversx_sc::types::OriginalResultMarker<#original_type>,
170 >
171 };
172
173 let msig = generate_proxy_method_sig(m, return_type);
174
175 let sig = quote! {
176 #[allow(clippy::too_many_arguments)]
177 #[allow(clippy::type_complexity)]
178 #msig {
179 multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc()
180 .to(self.extract_proxy_to())
181 .original_result()
182 .raw_call(#endpoint_name)
183 #payment_init
184 #(#arg_push_snippets)*
185 }
186 };
187
188 sig
189}
190
191pub fn generate_proxy_deploy(init_method: &Method) -> proc_macro2::TokenStream {
192 let mut payment_count = 0;
193 let mut multi_count = 0;
194 let mut token_count = 0;
195 let mut nonce_count = 0;
196
197 let mut payment_type = quote! { () };
198 let mut payment_init = quote! {};
199
200 let mut arg_push_snippets = Vec::<proc_macro2::TokenStream>::new();
201
202 for arg in &init_method.method_args {
203 match &arg.metadata.payment {
204 ArgPaymentMetadata::NotPayment => {
205 let pat = &arg.pat;
206 arg_push_snippets.push(quote! {
207 .argument(&#pat)
208 });
209 }
210 ArgPaymentMetadata::PaymentToken => {
211 token_count += 1;
212 }
213 ArgPaymentMetadata::PaymentNonce => {
214 nonce_count += 1;
215 }
216 ArgPaymentMetadata::PaymentAmount => {
217 payment_count += 1;
218 let payment_expr = &arg.pat;
219 payment_type = quote! { multiversx_sc::types::EgldPayment<Self::Api> };
220 payment_init = quote! { .egld(#payment_expr) };
221 }
222 ArgPaymentMetadata::PaymentMulti => {
223 multi_count += 1;
224 }
225 }
226 }
227
228 assert!(
229 payment_count <= 1,
230 "No more than one payment argument allowed in call proxy"
231 );
232 assert!(token_count == 0, "No ESDT payment allowed in #[init]");
233 assert!(nonce_count == 0, "No SFT/NFT payment allowed in #[init]");
234 assert!(
235 multi_count == 0,
236 "No multi ESDT payments allowed in #[init]"
237 );
238
239 let original_type = original_type_tokens(init_method);
240 let return_type = quote! {
241 multiversx_sc::types::Tx<
242 multiversx_sc::types::TxScEnv<Self::Api>,
243 (),
244 Self::To, #payment_type,
246 (),
247 multiversx_sc::types::DeployCall<multiversx_sc::types::TxScEnv<Self::Api>, ()>,
248 multiversx_sc::types::OriginalResultMarker<#original_type>,
249 >
250 };
251
252 let msig = generate_proxy_method_sig(init_method, return_type);
253
254 let sig = quote! {
255 #[allow(clippy::too_many_arguments)]
256 #[allow(clippy::type_complexity)]
257 #msig {
258 multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc()
259 .raw_deploy()
260 #payment_init
261 #(#arg_push_snippets)*
262 .original_result()
263 .to(self.extract_proxy_to()) }
265 };
266
267 sig
268}
269
270pub fn generate_method_impl(contract_trait: &ContractTrait) -> Vec<proc_macro2::TokenStream> {
271 contract_trait
272 .methods
273 .iter()
274 .filter_map(|m| match &m.public_role {
275 PublicRole::Init(_) => Some(generate_proxy_deploy(m)),
276 PublicRole::Upgrade(_) => Some(generate_proxy_endpoint(m, "upgrade".to_string())),
277 PublicRole::Endpoint(endpoint_metadata) => Some(generate_proxy_endpoint(
278 m,
279 endpoint_metadata.public_name.to_string(),
280 )),
281 _ => None,
282 })
283 .collect()
284}
285
286pub fn proxy_trait(contract: &ContractTrait) -> proc_macro2::TokenStream {
287 let proxy_supertrait_decl =
288 supertrait_gen::proxy_supertrait_decl(contract.supertraits.as_slice());
289 let proxy_methods_impl = generate_method_impl(contract);
290 quote! {
291 pub trait ProxyTrait:
292 multiversx_sc::contract_base::ProxyObjBase
293 + Sized
294 #(#proxy_supertrait_decl)*
295 {
296 #(#proxy_methods_impl)*
297 }
298 }
299}
300
301pub fn proxy_obj_code(contract: &ContractTrait) -> proc_macro2::TokenStream {
302 let proxy_object_def = snippets::proxy_object_def();
303 let impl_all_proxy_traits =
304 supertrait_gen::impl_all_proxy_traits(contract.supertraits.as_slice());
305 quote! {
306 #proxy_object_def
307
308 #(#impl_all_proxy_traits)*
309 }
310}
311
312fn equivalent_encode_path_gen(ty: &syn::Type) -> syn::Path {
313 let owned_type = convert_to_owned_type(ty);
314 syn::parse_str(
315 format!(
316 "multiversx_sc::types::ProxyArg<{}>",
317 owned_type.to_token_stream()
318 )
319 .as_str(),
320 )
321 .unwrap()
322}