cainome_parser/abi/
parser_legacy.rs

1use starknet::core::types::contract::legacy::{
2    RawLegacyAbiEntry, RawLegacyMember, RawLegacyStruct,
3};
4use starknet::core::types::contract::StateMutability;
5use std::collections::HashMap;
6
7use crate::tokens::{Composite, CompositeType, CoreBasic, Function, Token};
8use crate::{CainomeResult, Error, TokenizedAbi};
9
10pub struct AbiParserLegacy {}
11
12impl AbiParserLegacy {
13    /// Generates the [`Token`]s from the given ABI string.
14    ///
15    /// # Arguments
16    ///
17    /// * `abi` - A string representing the ABI (a JSON array of `RawLegacyAbiEntry`).
18    /// * `type_aliases` - Types to be renamed to avoid name clashing of generated types.
19    pub fn tokens_from_abi_string(
20        abi: &str,
21        type_aliases: &HashMap<String, String>,
22    ) -> CainomeResult<TokenizedAbi> {
23        let abi_entries = Self::parse_abi_string(abi)?;
24        let tokenized_abi =
25            Self::collect_tokens(&abi_entries, type_aliases).expect("failed tokens parsing");
26
27        Ok(tokenized_abi)
28    }
29
30    /// Parses an ABI string to output a `Vec<RawLegacyAbiEntry>`.
31    ///
32    /// # Arguments
33    ///
34    /// * `abi` - A string representing the ABI (a JSON array of `RawLegacyAbiEntry`).
35    pub fn parse_abi_string(abi: &str) -> CainomeResult<Vec<RawLegacyAbiEntry>> {
36        let entries =
37            serde_json::from_str::<Vec<RawLegacyAbiEntry>>(abi).map_err(Error::SerdeJson)?;
38        Ok(entries)
39    }
40
41    /// Parse all tokens in the ABI.
42    pub fn collect_tokens(
43        entries: &[RawLegacyAbiEntry],
44        type_aliases: &HashMap<String, String>,
45    ) -> CainomeResult<TokenizedAbi> {
46        let mut tokens: HashMap<String, Token> = HashMap::new();
47
48        for entry in entries {
49            Self::collect_entry_token(entry, &mut tokens)?;
50        }
51
52        let mut structs = vec![];
53        let mut enums = vec![];
54        // This is not memory efficient, but
55        // currently the focus is on search speed.
56        // To be optimized.
57        let mut all_composites: HashMap<String, Composite> = HashMap::new();
58
59        // Apply type aliases only on structs and enums.
60        for (_, mut t) in tokens {
61            for (type_path, alias) in type_aliases {
62                t.apply_alias(type_path, alias);
63            }
64
65            if let Token::Composite(ref c) = t {
66                all_composites.insert(c.type_path_no_generic(), c.clone());
67
68                match c.r#type {
69                    CompositeType::Struct => structs.push(t),
70                    CompositeType::Enum => enums.push(t),
71                    _ => (),
72                }
73            }
74        }
75
76        let mut functions = vec![];
77
78        for entry in entries {
79            Self::collect_entry_function(entry, &mut all_composites, &mut structs, &mut functions)?;
80        }
81
82        let interfaces: HashMap<String, Vec<Token>> = HashMap::new();
83
84        Ok(TokenizedAbi {
85            enums,
86            structs,
87            functions,
88            interfaces,
89        })
90    }
91
92    /// Collects the token from the ABI entry.
93    ///
94    /// # Arguments
95    ///
96    /// * `entry` - The ABI entry to collect tokens from.
97    /// * `tokens` - The list of tokens already collected.
98    fn collect_entry_token(
99        entry: &RawLegacyAbiEntry,
100        tokens: &mut HashMap<String, Token>,
101    ) -> CainomeResult<()> {
102        match entry {
103            RawLegacyAbiEntry::Struct(s) => {
104                // Some struct may be basics, we want to skip them.
105                if CoreBasic::parse(&s.name).is_ok() {
106                    return Ok(());
107                };
108
109                let token: Token = s.try_into()?;
110                tokens.insert(token.type_path(), token);
111            }
112            RawLegacyAbiEntry::Event(ev) => {
113                let token: Token = ev.try_into()?;
114                tokens.insert(token.type_path(), token);
115            }
116            _ => (),
117        };
118
119        Ok(())
120    }
121
122    /// Collects the function from the ABI entry.
123    ///
124    /// # Arguments
125    ///
126    /// * `entry` - The ABI entry to collect functions from.
127    /// * `all_composites` - All known composites tokens.
128    /// * `structs` - The list of structs already collected.
129    /// * `functions` - The list of functions already collected.
130    fn collect_entry_function(
131        entry: &RawLegacyAbiEntry,
132        all_composites: &mut HashMap<String, Composite>,
133        structs: &mut Vec<Token>,
134        functions: &mut Vec<Token>,
135    ) -> CainomeResult<()> {
136        /// Gets the existing token into known composite, if any.
137        /// Otherwise, return the parsed token.
138        fn get_existing_token_or_parsed(
139            type_path: &str,
140            all_composites: &HashMap<String, Composite>,
141        ) -> CainomeResult<Token> {
142            let parsed_token = Token::parse(type_path)?;
143
144            // If the token is an known struct or enum, we look up
145            // in existing one to get full info from there as the parsing
146            // of composites is already done before functions.
147            if let Token::Composite(ref c) = parsed_token {
148                match all_composites.get(&c.type_path_no_generic()) {
149                    Some(e) => Ok(Token::Composite(e.clone())),
150                    None => Ok(parsed_token),
151                }
152            } else {
153                Ok(parsed_token)
154            }
155        }
156
157        // TODO: optimize the search and data structures.
158        // HashMap would be more appropriate than vec.
159        if let RawLegacyAbiEntry::Function(f) = entry {
160            // Looks like in Cairo 0 ABI, if no mutability is given, it's an external.
161            let mutability = match f.state_mutability {
162                Some(_) => StateMutability::View,
163                None => StateMutability::External,
164            };
165
166            let mut func = Function::new(&f.name, mutability.into());
167
168            for i in &f.inputs {
169                let token = get_existing_token_or_parsed(&i.r#type, all_composites)?;
170                func.inputs.push((i.name.clone(), token));
171            }
172
173            for o in &f.outputs {
174                let token = get_existing_token_or_parsed(&o.r#type, all_composites)?;
175                func.named_outputs.push((o.name.clone(), token));
176            }
177
178            if !func.named_outputs.is_empty() {
179                let mut members = vec![];
180
181                for (offset, (n, t)) in func.named_outputs.iter().enumerate() {
182                    members.push(RawLegacyMember {
183                        name: n.clone(),
184                        offset: offset.try_into().unwrap(),
185                        r#type: t.type_path().clone(),
186                    });
187                }
188
189                let s = RawLegacyStruct {
190                    members,
191                    name: func.get_cairo0_output_name(),
192                    size: func.named_outputs.len() as u64,
193                };
194
195                structs.push((&s).try_into()?);
196            }
197
198            functions.push(Token::Function(func));
199        }
200
201        Ok(())
202    }
203}