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