multiversx_sc_meta_lib/contract/generate_snippets/
snippet_template_gen.rs

1use std::{fs::File, io::Write};
2
3use multiversx_sc::abi::ContractAbi;
4
5use super::snippet_gen_common::write_newline;
6
7pub(crate) fn write_snippet_imports(file: &mut File) {
8    writeln!(
9        file,
10        "#![allow(non_snake_case)]
11
12pub mod config;
13mod proxy;
14
15use config::Config;
16use multiversx_sc_snippets::imports::*;
17use serde::{{Deserialize, Serialize}};
18use std::{{
19    io::{{Read, Write}},
20    path::Path,
21}};"
22    )
23    .unwrap();
24
25    write_newline(file);
26}
27
28pub(crate) fn write_snippet_constants(file: &mut File) {
29    writeln!(file, "const STATE_FILE: &str = \"state.toml\";").unwrap();
30}
31
32pub(crate) fn write_snippet_main_function(file: &mut File, abi: &ContractAbi, crate_name: &str) {
33    writeln!(
34        file,
35        "
36pub async fn {crate_name}_cli() {{
37    env_logger::init();
38
39    let mut args = std::env::args();
40    let _ = args.next();
41    let cmd = args.next().expect(\"at least one argument required\");
42    let config = Config::new();
43    let mut interact = ContractInteract::new(config).await;
44    match cmd.as_str() {{"
45    )
46    .unwrap();
47
48    // all contracts have a deploy snippet
49    writeln!(file, r#"        "deploy" => interact.deploy().await,"#).unwrap();
50
51    for upgrade_endpoint in &abi.upgrade_constructors {
52        writeln!(
53            file,
54            r#"        "{}" => interact.{}().await,"#,
55            upgrade_endpoint.name, upgrade_endpoint.rust_method_name
56        )
57        .unwrap();
58    }
59
60    for endpoint in &abi.endpoints {
61        writeln!(
62            file,
63            r#"        "{}" => interact.{}().await,"#,
64            endpoint.name, endpoint.rust_method_name
65        )
66        .unwrap();
67    }
68
69    // general case of "command not found" + close curly brackets
70    writeln!(
71        file,
72        "        _ => panic!(\"unknown command: {{}}\", &cmd),
73    }}
74}}"
75    )
76    .unwrap();
77}
78
79pub(crate) fn write_interact_struct_declaration(file: &mut File) {
80    writeln!(
81        file,
82        "pub struct ContractInteract {{
83    interactor: Interactor,
84    wallet_address: Address,
85    contract_code: BytesValue,
86    state: State
87}}"
88    )
89    .unwrap();
90
91    write_newline(file);
92}
93
94pub(crate) fn write_state_struct_declaration(file: &mut File) {
95    writeln!(
96        file,
97        "
98#[derive(Debug, Default, Serialize, Deserialize)]
99pub struct State {{
100    contract_address: Option<Bech32Address>
101}}"
102    )
103    .unwrap();
104
105    write_newline(file);
106}
107
108pub(crate) fn write_snippet_state_impl(file: &mut File) {
109    writeln!(
110        file,
111        r#"impl State {{
112        // Deserializes state from file
113        pub fn load_state() -> Self {{
114            if Path::new(STATE_FILE).exists() {{
115                let mut file = std::fs::File::open(STATE_FILE).unwrap();
116                let mut content = String::new();
117                file.read_to_string(&mut content).unwrap();
118                toml::from_str(&content).unwrap()
119            }} else {{
120                Self::default()
121            }}
122        }}
123    
124        /// Sets the contract address
125        pub fn set_address(&mut self, address: Bech32Address) {{
126            self.contract_address = Some(address);
127        }}
128    
129        /// Returns the contract address
130        pub fn current_address(&self) -> &Bech32Address {{
131            self.contract_address
132                .as_ref()
133                .expect("no known contract, deploy first")
134        }}
135    }}
136    
137    impl Drop for State {{
138        // Serializes state to file
139        fn drop(&mut self) {{
140            let mut file = std::fs::File::create(STATE_FILE).unwrap();
141            file.write_all(toml::to_string(self).unwrap().as_bytes())
142                .unwrap();
143        }}
144    }}"#
145    )
146    .unwrap();
147
148    write_newline(file);
149}
150
151pub(crate) fn write_config_imports(file: &mut File) {
152    writeln!(
153        file,
154        "#![allow(unused)]
155
156use serde::Deserialize;
157use std::io::Read;
158"
159    )
160    .unwrap();
161}
162
163pub(crate) fn write_config_constants(file: &mut File) {
164    writeln!(
165        file,
166        "/// Config file
167const CONFIG_FILE: &str = \"config.toml\";
168"
169    )
170    .unwrap();
171}
172
173pub(crate) fn write_config_struct_declaration(file: &mut File) {
174    writeln!(
175        file,
176        r#"#[derive(Debug, Deserialize)]
177#[serde(rename_all = "lowercase")]
178pub enum ChainType {{
179    Real,
180    Simulator,
181}}
182
183/// Contract Interact configuration
184#[derive(Debug, Deserialize)]
185pub struct Config {{
186    pub gateway_uri: String,
187    pub chain_type: ChainType,
188}}
189"#
190    )
191    .unwrap();
192}
193
194pub(crate) fn write_config_struct_impl(file: &mut File) {
195    writeln!(
196        file,
197        r#"impl Config {{
198    // Deserializes config from file
199    pub fn new() -> Self {{
200        let mut file = std::fs::File::open(CONFIG_FILE).unwrap();
201        let mut content = String::new();
202        file.read_to_string(&mut content).unwrap();
203        toml::from_str(&content).unwrap()
204    }}
205
206    pub fn chain_simulator_config() -> Self {{
207        Config {{
208            gateway_uri: "http://localhost:8085".to_owned(),
209            chain_type: ChainType::Simulator,
210        }}
211    }}
212
213    // Returns the gateway URI
214    pub fn gateway_uri(&self) -> &str {{
215        &self.gateway_uri
216    }}
217
218    // Returns if chain type is chain simulator
219    pub fn use_chain_simulator(&self) -> bool {{
220        match self.chain_type {{
221            ChainType::Real => false,
222            ChainType::Simulator => true,
223        }}
224    }}
225}}"#
226    )
227    .unwrap();
228}
229
230pub(crate) fn write_chain_sim_test_to_file(file: &mut File, crate_name: &str) {
231    writeln!(
232        file,
233        r#"use multiversx_sc_snippets::imports::*;
234use rust_interact::{{config::Config, ContractInteract}};
235
236// Simple deploy test that runs using the chain simulator configuration.
237// In order for this test to work, make sure that the `config.toml` file contains the chain simulator config (or choose it manually)
238// The chain simulator should already be installed and running before attempting to run this test.
239// The chain-simulator-tests feature should be present in Cargo.toml.
240// Can be run with `sc-meta test -c`.
241#[tokio::test]
242#[cfg_attr(not(feature = "chain-simulator-tests"), ignore)]
243async fn deploy_test_{crate_name}_cs() {{
244    let mut interactor = ContractInteract::new(Config::chain_simulator_config()).await;
245
246    interactor.deploy().await;
247}}"#
248    ).unwrap()
249}
250
251pub(crate) fn write_interactor_test_to_file(file: &mut File, crate_name: &str) {
252    writeln!(
253        file,
254        r#"use multiversx_sc_snippets::imports::*;
255use rust_interact::{{config::Config, ContractInteract}};
256
257// Simple deploy test that runs on the real blockchain configuration.
258// In order for this test to work, make sure that the `config.toml` file contains the real blockchain config (or choose it manually)
259// Can be run with `sc-meta test`.
260#[tokio::test]
261#[ignore = "run on demand, relies on real blockchain state"]
262async fn deploy_test_{crate_name}() {{
263    let mut interactor = ContractInteract::new(Config::new()).await;
264
265    interactor.deploy().await;
266}}"#
267    ).unwrap()
268}