multiversx_sc_meta_lib/contract/sc_config/
contract_variant.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
use std::path::PathBuf;

use super::{contract_variant_builder::default_wasm_crate_name, ContractVariantSettings};
use crate::cli::BuildArgs;
use multiversx_sc::abi::ContractAbi;

/// Represents a contract created by the framework when building.
///
/// It might have only some of the endpoints written by the developer and maybe some other function.
pub struct ContractVariant {
    /// If it is the main contract, then the wasm crate is called just `wasm`,
    ///and the wasm `Cargo.toml` is provided by the dev.
    pub main: bool,

    /// The contract id is defined in `multicontract.toml`. It has no effect on the produced assets.
    ///
    /// It can be the same as the contract name, but it is not necessary.
    pub contract_id: String,

    /// The name, as seen in the generated contract names.
    ///
    /// It is either defined in the multicontract.toml, or is inferred from the main crate name.
    pub contract_name: String,

    /// The name of the wasm crate, as it appear in Cargo.toml. It is normally the `contract_name` field, followed by the `-wasm` suffix.
    ///
    /// However, the main contract Cargo.toml is given explicitly, so this name might differ.
    pub wasm_crate_name: String,

    /// Collection of flags, specified in the multicontract config.
    pub settings: ContractVariantSettings,

    /// Filtered and processed ABI of the output contract.
    pub abi: ContractAbi,
}

impl ContractVariant {
    pub fn default_from_abi(abi: &ContractAbi) -> Self {
        let default_contract_config_name = abi.build_info.contract_crate.name.to_string();
        let wasm_crate_name = default_wasm_crate_name(&default_contract_config_name);
        ContractVariant {
            main: true,
            settings: ContractVariantSettings::default(),
            contract_id: default_contract_config_name.clone(),
            contract_name: default_contract_config_name,
            wasm_crate_name,
            abi: abi.clone(),
        }
    }

    pub fn public_name_snake_case(&self) -> String {
        self.contract_name.replace('-', "_")
    }

    /// The name of the directory of the wasm crate.
    ///
    /// Note this does not necessarily have to match the wasm crate name defined in Cargo.toml.
    pub fn wasm_crate_dir_name(&self) -> String {
        if self.main {
            "wasm".to_string()
        } else {
            format!("wasm-{}", &self.contract_name)
        }
    }

    pub fn wasm_crate_path(&self) -> String {
        format!("../{}", &self.wasm_crate_dir_name())
    }

    pub fn cargo_toml_path(&self) -> String {
        format!("{}/Cargo.toml", &self.wasm_crate_path())
    }

    pub fn some_other_test_path(&self) -> String {
        format!("{}/test-Cargo.toml", &self.wasm_crate_path())
    }

    pub fn wasm_crate_name_snake_case(&self) -> String {
        self.wasm_crate_name.replace('-', "_")
    }

    pub fn resolve_wasm_target_dir(&self, explicit_target_dir: &Option<String>) -> String {
        let wasm_crate_path = self.wasm_crate_path();
        if let Some(explicit_target_dir) = explicit_target_dir {
            // usually the explicit_target_dir is absolute,
            // but if it isn't, we need to take the path of the wasm crate into account
            PathBuf::from(wasm_crate_path)
                .join(explicit_target_dir)
                .to_str()
                .unwrap()
                .to_string()
        } else {
            format!("{}/target", &wasm_crate_path)
        }
    }

    /// This is where Rust will initially compile the WASM binary.
    pub fn wasm_compilation_output_path(&self, explicit_target_dir: &Option<String>) -> String {
        let target_dir = self.resolve_wasm_target_dir(explicit_target_dir);

        format!(
            "{}/wasm32-unknown-unknown/release/{}.wasm",
            &target_dir,
            &self.wasm_crate_name_snake_case(),
        )
    }

    pub fn abi_output_name(&self) -> String {
        format!("{}.abi.json", &self.contract_name)
    }

    fn output_name_base(&self, build_args: &BuildArgs) -> String {
        if let Some(wasm_name_override) = &build_args.wasm_name_override {
            wasm_name_override.clone()
        } else if let Some(suffix) = &build_args.wasm_name_suffix {
            format!("{}-{suffix}", &self.contract_name)
        } else {
            self.contract_name.clone()
        }
    }

    pub fn wasm_output_name(&self, build_args: &BuildArgs) -> String {
        format!("{}.wasm", self.output_name_base(build_args))
    }

    pub fn wat_output_name(&self, build_args: &BuildArgs) -> String {
        format!("{}.wat", self.output_name_base(build_args))
    }

    pub fn mxsc_file_output_name(&self, build_args: &BuildArgs) -> String {
        format!("{}.mxsc.json", self.output_name_base(build_args))
    }

    pub fn imports_json_output_name(&self, build_args: &BuildArgs) -> String {
        format!("{}.imports.json", self.output_name_base(build_args))
    }

    pub fn twiggy_top_name(&self, build_args: &BuildArgs) -> String {
        format!("twiggy-top-{}.txt", self.output_name_base(build_args))
    }

    pub fn twiggy_paths_name(&self, build_args: &BuildArgs) -> String {
        format!("twiggy-paths-{}.txt", self.output_name_base(build_args))
    }

    pub fn twiggy_monos_name(&self, build_args: &BuildArgs) -> String {
        format!("twiggy-monos-{}.txt", self.output_name_base(build_args))
    }

    pub fn twiggy_dominators_name(&self, build_args: &BuildArgs) -> String {
        format!(
            "twiggy-dominators-{}.txt",
            self.output_name_base(build_args)
        )
    }

    pub fn endpoint_names(&self) -> Vec<String> {
        self.abi
            .endpoints
            .iter()
            .map(|endpoint| endpoint.name.to_string())
            .collect()
    }

    /// Yields "init" + all endpoint names + "callBack" (if it exists).
    ///
    /// Should correspond to all wasm exported functions.
    pub fn all_exported_function_names(&self) -> Vec<String> {
        let mut result = vec!["init".to_string()];
        if !self.abi.upgrade_constructors.is_empty() {
            result.push("upgrade".to_string())
        }
        result.append(&mut self.endpoint_names());
        if self.abi.has_callback {
            result.push("callBack".to_string());
        }
        result
    }
}

impl std::fmt::Debug for ContractVariant {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("ContractVariant")
            .field("main", &self.main)
            .field("config_name", &self.contract_id)
            .field("public_name", &self.contract_name)
            .field("num-constructors", &self.abi.constructors.len())
            .field(
                "num-upgrade-constructors",
                &self.abi.upgrade_constructors.len(),
            )
            .field("num-endpoints", &self.abi.endpoints.len())
            .field("settings", &self.settings)
            .finish()
    }
}