multiversx_sc_meta_lib/contract/generate_snippets/
snippet_sc_functions_gen.rs1use std::{fs::File, io::Write, path::Path};
2
3use multiversx_sc::abi::{ContractAbi, EndpointAbi, EndpointMutabilityAbi, InputAbi};
4
5use super::{snippet_gen_common::write_newline, snippet_type_map::map_abi_type_to_rust_type};
6
7const DEFAULT_GAS: &str = "30_000_000u64";
8
9pub(crate) fn write_interact_struct_impl(file: &mut File, abi: &ContractAbi, crate_name: &str) {
10 let crate_path = crate_name.replace("_", "-");
11 let mxsc_file_name = format!("{crate_path}.mxsc.json");
12 let wasm_output_file_path = Path::new("..").join("output").join(mxsc_file_name);
13
14 let wasm_output_file_path_expr =
15 format!("\"mxsc:{}\"", &wasm_output_file_path.to_string_lossy());
16
17 writeln!(
18 file,
19 r#"impl ContractInteract {{
20 pub async fn new(config: Config) -> Self {{
21 let mut interactor = Interactor::new(config.gateway_uri())
22 .await
23 .use_chain_simulator(config.use_chain_simulator());
24
25 interactor.set_current_dir_from_workspace("{}");
26 let wallet_address = interactor.register_wallet(test_wallets::alice()).await;
27
28 // Useful in the chain simulator setting
29 // generate blocks until ESDTSystemSCAddress is enabled
30 interactor.generate_blocks_until_all_activations().await;
31
32 let contract_code = BytesValue::interpret_from(
33 {},
34 &InterpreterContext::default(),
35 );
36
37 ContractInteract {{
38 interactor,
39 wallet_address,
40 contract_code,
41 state: State::load_state()
42 }}
43 }}
44"#,
45 crate_path, wasm_output_file_path_expr,
46 )
47 .unwrap();
48 write_deploy_method_impl(file, &abi.constructors[0], &abi.name);
49
50 for upgrade_abi in &abi.upgrade_constructors {
51 write_upgrade_endpoint_impl(file, upgrade_abi, &abi.name);
52 }
53
54 for endpoint_abi in &abi.endpoints {
55 write_endpoint_impl(file, endpoint_abi, &abi.name);
56 }
57
58 writeln!(file, "}}").unwrap();
60}
61
62fn write_deploy_method_impl(file: &mut File, init_abi: &EndpointAbi, name: &String) {
63 write_method_declaration(file, "deploy");
64 write_endpoint_args_declaration(file, &init_abi.inputs);
65 let proxy_name = format!("{}Proxy", name);
66
67 writeln!(
68 file,
69 r#" let new_address = self
70 .interactor
71 .tx()
72 .from(&self.wallet_address)
73 .gas({DEFAULT_GAS})
74 .typed(proxy::{})
75 .init({})
76 .code(&self.contract_code)
77 .returns(ReturnsNewAddress)
78 .run()
79 .await;
80 let new_address_bech32 = new_address.to_bech32_default();
81 println!("new address: {{new_address_bech32}}");
82 self.state.set_address(new_address_bech32);"#,
83 proxy_name,
84 endpoint_args_when_called(init_abi.inputs.as_slice()),
85 )
86 .unwrap();
87
88 writeln!(file, " }}").unwrap();
90 write_newline(file);
91}
92
93fn write_upgrade_endpoint_impl(file: &mut File, upgrade_abi: &EndpointAbi, name: &String) {
94 write_method_declaration(file, "upgrade");
95 write_endpoint_args_declaration(file, &upgrade_abi.inputs);
96 let proxy_name = format!("{}Proxy", name);
97
98 writeln!(
99 file,
100 r#" let response = self
101 .interactor
102 .tx()
103 .to(self.state.current_address())
104 .from(&self.wallet_address)
105 .gas({DEFAULT_GAS})
106 .typed(proxy::{})
107 .upgrade({})
108 .code(&self.contract_code)
109 .code_metadata(CodeMetadata::UPGRADEABLE)
110 .returns(ReturnsResultUnmanaged)
111 .run()
112 .await;
113
114 println!("Result: {{response:?}}");"#,
115 proxy_name,
116 endpoint_args_when_called(upgrade_abi.inputs.as_slice()),
117 )
118 .unwrap();
119
120 writeln!(file, " }}").unwrap();
122 write_newline(file);
123}
124
125fn write_endpoint_impl(file: &mut File, endpoint_abi: &EndpointAbi, name: &String) {
126 write_method_declaration(file, &endpoint_abi.rust_method_name);
127 write_payments_declaration(file, &endpoint_abi.payable_in_tokens);
128 write_endpoint_args_declaration(file, &endpoint_abi.inputs);
129 if matches!(endpoint_abi.mutability, EndpointMutabilityAbi::Readonly) {
130 write_contract_query(file, endpoint_abi, name);
131 } else {
132 write_contract_call(file, endpoint_abi, name);
133 }
134
135 writeln!(file, " }}").unwrap();
137 write_newline(file);
138}
139
140fn write_method_declaration(file: &mut File, endpoint_name: &str) {
141 writeln!(file, " pub async fn {endpoint_name}(&mut self) {{").unwrap();
142}
143
144fn write_payments_declaration(file: &mut File, accepted_tokens: &[String]) {
145 if accepted_tokens.is_empty() {
146 return;
147 }
148
149 let biguint_default = map_abi_type_to_rust_type("BigUint".to_string());
151 let first_accepted = &accepted_tokens[0];
152 if first_accepted == "EGLD" {
153 writeln!(
154 file,
155 " let egld_amount = {};",
156 biguint_default.get_default_value_expr()
157 )
158 .unwrap();
159 } else {
160 writeln!(
161 file,
162 " let token_id = String::new();
163 let token_nonce = 0u64;
164 let token_amount = {};",
165 biguint_default.get_default_value_expr()
166 )
167 .unwrap();
168 }
169
170 write_newline(file);
171}
172
173fn write_endpoint_args_declaration(file: &mut File, inputs: &[InputAbi]) {
174 if inputs.is_empty() {
175 return;
176 }
177
178 for input in inputs {
179 let rust_type = map_abi_type_to_rust_type(input.type_names.abi.clone());
180 writeln!(
181 file,
182 " let {} = {};",
183 input.arg_name,
184 rust_type.get_default_value_expr()
185 )
186 .unwrap();
187 }
188
189 write_newline(file);
190}
191
192fn endpoint_args_when_called(inputs: &[InputAbi]) -> String {
193 let mut result = String::new();
194 for input in inputs {
195 if !result.is_empty() {
196 result.push_str(", ");
197 }
198 result.push_str(&input.arg_name);
199 }
200 result
201}
202
203fn write_contract_call(file: &mut File, endpoint_abi: &EndpointAbi, name: &String) {
204 let payment_snippet = if endpoint_abi.payable_in_tokens.is_empty() {
205 ""
206 } else if endpoint_abi.payable_in_tokens[0] == "EGLD" {
207 "\n .egld(egld_amount)"
208 } else {
209 "\n .payment((TokenIdentifier::from(token_id.as_str()), token_nonce, token_amount))"
210 };
211
212 writeln!(
213 file,
214 r#" let response = self
215 .interactor
216 .tx()
217 .from(&self.wallet_address)
218 .to(self.state.current_address())
219 .gas({DEFAULT_GAS})
220 .typed(proxy::{}Proxy)
221 .{}({}){}
222 .returns(ReturnsResultUnmanaged)
223 .run()
224 .await;
225
226 println!("Result: {{response:?}}");"#,
227 name,
228 endpoint_abi.rust_method_name,
229 endpoint_args_when_called(endpoint_abi.inputs.as_slice()),
230 payment_snippet,
231 )
232 .unwrap();
233}
234
235fn write_contract_query(file: &mut File, endpoint_abi: &EndpointAbi, name: &String) {
236 writeln!(
237 file,
238 r#" let result_value = self
239 .interactor
240 .query()
241 .to(self.state.current_address())
242 .typed(proxy::{}Proxy)
243 .{}({})
244 .returns(ReturnsResultUnmanaged)
245 .run()
246 .await;
247
248 println!("Result: {{result_value:?}}");"#,
249 name,
250 endpoint_abi.rust_method_name,
251 endpoint_args_when_called(endpoint_abi.inputs.as_slice()),
252 )
253 .unwrap();
254}