multiversx_sc_meta_lib/abi_json/
contract_abi_json.rs

1use super::*;
2use multiversx_sc::abi::*;
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6#[derive(Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub struct ContractAbiJson {
9    #[serde(default)]
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub build_info: Option<BuildInfoAbiJson>,
12
13    #[serde(default)]
14    #[serde(skip_serializing_if = "Vec::is_empty")]
15    pub docs: Vec<String>,
16
17    pub name: String,
18
19    #[serde(default)]
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub constructor: Option<ConstructorAbiJson>,
22
23    #[serde(default)]
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub upgrade_constructor: Option<ConstructorAbiJson>,
26
27    #[serde(default)]
28    pub endpoints: Vec<EndpointAbiJson>,
29
30    #[serde(default)]
31    #[serde(skip_serializing_if = "Vec::is_empty")]
32    pub promises_callback_names: Vec<String>,
33
34    #[serde(default)]
35    #[serde(skip_serializing_if = "Vec::is_empty")]
36    pub events: Vec<EventAbiJson>,
37
38    #[serde(default)]
39    pub esdt_attributes: Vec<EsdtAttributeJson>,
40
41    #[serde(default)]
42    pub has_callback: bool,
43
44    #[serde(default)]
45    pub types: BTreeMap<String, TypeDescriptionJson>,
46}
47
48impl From<ContractAbiJson> for ContractAbi {
49    fn from(abi_json: ContractAbiJson) -> Self {
50        ContractAbi {
51            build_info: abi_json
52                .build_info
53                .map(BuildInfoAbi::from)
54                .unwrap_or_default(),
55            docs: abi_json.docs,
56            name: abi_json.name,
57            constructors: abi_json
58                .constructor
59                .map(|c| vec![EndpointAbi::from(&c)])
60                .unwrap_or_default(),
61            upgrade_constructors: abi_json
62                .upgrade_constructor
63                .map(|c| vec![EndpointAbi::from(&c)])
64                .unwrap_or_default(),
65            endpoints: abi_json
66                .endpoints
67                .into_iter()
68                .map(EndpointAbi::from)
69                .collect(),
70            promise_callbacks: abi_json
71                .promises_callback_names
72                .into_iter()
73                .map(|name| EndpointAbi {
74                    name,
75                    ..Default::default()
76                })
77                .collect(),
78            events: abi_json.events.into_iter().map(EventAbi::from).collect(),
79            esdt_attributes: abi_json
80                .esdt_attributes
81                .into_iter()
82                .map(EsdtAttributeAbi::from)
83                .collect(),
84            has_callback: abi_json.has_callback,
85            type_descriptions: convert_json_to_type_descriptions(abi_json.types),
86        }
87    }
88}
89
90impl From<&ContractAbi> for ContractAbiJson {
91    fn from(abi: &ContractAbi) -> Self {
92        ContractAbiJson {
93            build_info: Some(BuildInfoAbiJson::from(&abi.build_info)),
94            docs: abi.docs.iter().map(|d| d.to_string()).collect(),
95            name: abi.name.to_string(),
96            constructor: abi.constructors.first().map(ConstructorAbiJson::from),
97            upgrade_constructor: abi
98                .upgrade_constructors
99                .first()
100                .map(ConstructorAbiJson::from),
101            endpoints: abi.endpoints.iter().map(EndpointAbiJson::from).collect(),
102            promises_callback_names: abi
103                .promise_callbacks
104                .iter()
105                .map(|endpoint| endpoint.name.to_string())
106                .collect(),
107            events: abi.events.iter().map(EventAbiJson::from).collect(),
108            has_callback: abi.has_callback,
109            types: convert_type_descriptions_to_json(&abi.type_descriptions),
110            esdt_attributes: abi
111                .esdt_attributes
112                .iter()
113                .map(EsdtAttributeJson::from)
114                .collect(),
115        }
116    }
117}
118
119pub fn convert_type_descriptions_to_json(
120    type_descriptions: &TypeDescriptionContainerImpl,
121) -> BTreeMap<String, TypeDescriptionJson> {
122    let mut types = BTreeMap::new();
123    for (type_names, type_description) in type_descriptions.0.iter() {
124        if type_description.contents.is_specified() {
125            types.insert(
126                type_names.abi.clone(),
127                TypeDescriptionJson::from(type_description),
128            );
129        }
130    }
131    types
132}
133
134pub fn convert_json_to_type_descriptions(
135    types: BTreeMap<String, TypeDescriptionJson>,
136) -> TypeDescriptionContainerImpl {
137    let mut type_descriptions = TypeDescriptionContainerImpl::new();
138    for (type_name, type_description) in types.into_iter() {
139        type_descriptions.insert(
140            TypeNames::from_abi(type_name),
141            TypeDescription::from(&type_description),
142        );
143    }
144    type_descriptions
145}
146
147pub fn empty_type_description_container() -> TypeDescriptionContainerImpl {
148    TypeDescriptionContainerImpl::new()
149}
150
151pub fn serialize_abi_to_json(abi_json: &ContractAbiJson) -> String {
152    let buf = Vec::new();
153    let formatter = serde_json::ser::PrettyFormatter::with_indent(b"    ");
154    let mut ser = serde_json::Serializer::with_formatter(buf, formatter);
155    abi_json.serialize(&mut ser).unwrap();
156    let mut serialized = String::from_utf8(ser.into_inner()).unwrap();
157    serialized.push('\n');
158    serialized
159}
160
161pub fn deserialize_abi_from_json(input: &str) -> Result<ContractAbiJson, String> {
162    serde_json::from_str(input).map_err(|err| err.to_string())
163}
164
165#[cfg(test)]
166mod tests {
167    const MINIMAL_ABI_JSON: &str = r#"{
168        "name": "Minimal"
169    }"#;
170
171    #[test]
172    fn decode_minimal_contract_abi() {
173        super::deserialize_abi_from_json(MINIMAL_ABI_JSON).unwrap();
174    }
175}