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!(
137 multi_count == 0,
138 "#[payment_multi] cannot coexist with any other payment annotation in the same endpoint"
139 );
140
141 if token_count == 0 && nonce_count == 0 {
142 payment_type = quote! { multiversx_sc::types::EgldPayment<Self::Api> };
143 payment_init = quote! { .egld(#payment_expr) };
144 } else {
145 payment_type = quote! { multiversx_sc::types::EgldOrEsdtTokenPayment<Self::Api> };
146 payment_init = quote! { .payment(
147 multiversx_sc::types::EgldOrEsdtTokenPayment::new(
148 #token_expr,
149 #nonce_expr,
150 #payment_expr,
151 )
152 )};
153 }
154 } else if multi_count > 0 {
155 let multi_expr = multi_expr_opt.unwrap();
156 payment_type = quote! { EsdtTokenPaymentVec<Self::Api> };
157 payment_init = quote! { .payment(#multi_expr.clone_value()) };
158 } else {
159 payment_type = quote! { () };
160 payment_init = quote! {};
161 }
162
163 let original_type = original_type_tokens(m);
164 let return_type = quote! {
165 multiversx_sc::types::Tx<
166 multiversx_sc::types::TxScEnv<Self::Api>,
167 (),
168 Self::To,
169 #payment_type,
170 (),
171 multiversx_sc::types::FunctionCall<Self::Api>,
172 multiversx_sc::types::OriginalResultMarker<#original_type>,
173 >
174 };
175
176 let msig = generate_proxy_method_sig(m, return_type);
177
178 let sig = quote! {
179 #[allow(clippy::too_many_arguments)]
180 #[allow(clippy::type_complexity)]
181 #msig {
182 multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc()
183 .to(self.extract_proxy_to())
184 .original_result()
185 .raw_call(#endpoint_name)
186 #payment_init
187 #(#arg_push_snippets)*
188 }
189 };
190
191 sig
192}
193
194pub fn generate_proxy_deploy(init_method: &Method) -> proc_macro2::TokenStream {
195 let mut payment_count = 0;
196 let mut multi_count = 0;
197 let mut token_count = 0;
198 let mut nonce_count = 0;
199
200 let mut payment_type = quote! { () };
201 let mut payment_init = quote! {};
202
203 let mut arg_push_snippets = Vec::<proc_macro2::TokenStream>::new();
204
205 for arg in &init_method.method_args {
206 match &arg.metadata.payment {
207 ArgPaymentMetadata::NotPayment => {
208 let pat = &arg.pat;
209 arg_push_snippets.push(quote! {
210 .argument(&#pat)
211 });
212 }
213 ArgPaymentMetadata::PaymentToken => {
214 token_count += 1;
215 }
216 ArgPaymentMetadata::PaymentNonce => {
217 nonce_count += 1;
218 }
219 ArgPaymentMetadata::PaymentAmount => {
220 payment_count += 1;
221 let payment_expr = &arg.pat;
222 payment_type = quote! { multiversx_sc::types::EgldPayment<Self::Api> };
223 payment_init = quote! { .egld(#payment_expr) };
224 }
225 ArgPaymentMetadata::PaymentMulti => {
226 multi_count += 1;
227 }
228 }
229 }
230
231 assert!(
232 payment_count <= 1,
233 "No more than one payment argument allowed in call proxy"
234 );
235 assert!(token_count == 0, "No ESDT payment allowed in #[init]");
236 assert!(nonce_count == 0, "No SFT/NFT payment allowed in #[init]");
237 assert!(
238 multi_count == 0,
239 "No multi ESDT payments allowed in #[init]"
240 );
241
242 let original_type = original_type_tokens(init_method);
243 let return_type = quote! {
244 multiversx_sc::types::Tx<
245 multiversx_sc::types::TxScEnv<Self::Api>,
246 (),
247 Self::To, #payment_type,
249 (),
250 multiversx_sc::types::DeployCall<multiversx_sc::types::TxScEnv<Self::Api>, ()>,
251 multiversx_sc::types::OriginalResultMarker<#original_type>,
252 >
253 };
254
255 let msig = generate_proxy_method_sig(init_method, return_type);
256
257 let sig = quote! {
258 #[allow(clippy::too_many_arguments)]
259 #[allow(clippy::type_complexity)]
260 #msig {
261 multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc()
262 .raw_deploy()
263 #payment_init
264 #(#arg_push_snippets)*
265 .original_result()
266 .to(self.extract_proxy_to()) }
268 };
269
270 sig
271}
272
273pub fn generate_method_impl(contract_trait: &ContractTrait) -> Vec<proc_macro2::TokenStream> {
274 contract_trait
275 .methods
276 .iter()
277 .filter_map(|m| match &m.public_role {
278 PublicRole::Init(_) => Some(generate_proxy_deploy(m)),
279 PublicRole::Upgrade(_) => Some(generate_proxy_endpoint(m, "upgrade".to_string())),
280 PublicRole::Endpoint(endpoint_metadata) => Some(generate_proxy_endpoint(
281 m,
282 endpoint_metadata.public_name.to_string(),
283 )),
284 _ => None,
285 })
286 .collect()
287}
288
289pub fn proxy_trait(contract: &ContractTrait) -> proc_macro2::TokenStream {
290 let proxy_supertrait_decl =
291 supertrait_gen::proxy_supertrait_decl(contract.supertraits.as_slice());
292 let proxy_methods_impl = generate_method_impl(contract);
293 quote! {
294 pub trait ProxyTrait:
295 multiversx_sc::contract_base::ProxyObjBase
296 + Sized
297 #(#proxy_supertrait_decl)*
298 {
299 #(#proxy_methods_impl)*
300 }
301 }
302}
303
304pub fn proxy_obj_code(contract: &ContractTrait) -> proc_macro2::TokenStream {
305 let proxy_object_def = snippets::proxy_object_def();
306 let impl_all_proxy_traits =
307 supertrait_gen::impl_all_proxy_traits(contract.supertraits.as_slice());
308 quote! {
309 #proxy_object_def
310
311 #(#impl_all_proxy_traits)*
312 }
313}
314
315fn equivalent_encode_path_gen(ty: &syn::Type) -> syn::Path {
316 let owned_type = convert_to_owned_type(ty);
317 syn::parse_str(
318 format!(
319 "multiversx_sc::types::ProxyArg<{}>",
320 owned_type.to_token_stream()
321 )
322 .as_str(),
323 )
324 .unwrap()
325}