1use crate::{
2 config::{
3 EvmNetworkConfig, NetworkConfigCommon, NetworkFileConfig, SolanaNetworkConfig,
4 StellarNetworkConfig,
5 },
6 models::NetworkType,
7};
8use eyre;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub enum NetworkConfigData {
14 Evm(EvmNetworkConfig),
16 Solana(SolanaNetworkConfig),
18 Stellar(StellarNetworkConfig),
20}
21
22impl NetworkConfigData {
23 pub fn common(&self) -> &NetworkConfigCommon {
25 match self {
26 NetworkConfigData::Evm(config) => &config.common,
27 NetworkConfigData::Solana(config) => &config.common,
28 NetworkConfigData::Stellar(config) => &config.common,
29 }
30 }
31
32 pub fn network_type(&self) -> NetworkType {
34 match self {
35 NetworkConfigData::Evm(_) => NetworkType::Evm,
36 NetworkConfigData::Solana(_) => NetworkType::Solana,
37 NetworkConfigData::Stellar(_) => NetworkType::Stellar,
38 }
39 }
40
41 pub fn network_name(&self) -> &str {
43 &self.common().network
44 }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct NetworkRepoModel {
53 pub id: String,
55 pub name: String,
57 pub network_type: NetworkType,
59 pub config: NetworkConfigData,
61}
62
63impl NetworkRepoModel {
64 pub fn new_evm(config: EvmNetworkConfig) -> Self {
72 let name = config.common.network.clone();
73 let id = format!("evm:{}", name).to_lowercase();
74 Self {
75 id,
76 name,
77 network_type: NetworkType::Evm,
78 config: NetworkConfigData::Evm(config),
79 }
80 }
81
82 pub fn new_solana(config: SolanaNetworkConfig) -> Self {
90 let name = config.common.network.clone();
91 let id = format!("solana:{}", name).to_lowercase();
92 Self {
93 id,
94 name,
95 network_type: NetworkType::Solana,
96 config: NetworkConfigData::Solana(config),
97 }
98 }
99
100 pub fn new_stellar(config: StellarNetworkConfig) -> Self {
108 let name = config.common.network.clone();
109 let id = format!("stellar:{}", name).to_lowercase();
110 Self {
111 id,
112 name,
113 network_type: NetworkType::Stellar,
114 config: NetworkConfigData::Stellar(config),
115 }
116 }
117
118 pub fn create_id(network_type: NetworkType, name: &str) -> String {
127 format!("{:?}:{}", network_type, name).to_lowercase()
128 }
129
130 pub fn common(&self) -> &NetworkConfigCommon {
132 self.config.common()
133 }
134
135 pub fn config(&self) -> &NetworkConfigData {
137 &self.config
138 }
139}
140
141impl TryFrom<NetworkFileConfig> for NetworkRepoModel {
142 type Error = eyre::Report;
143
144 fn try_from(network_config: NetworkFileConfig) -> Result<Self, Self::Error> {
152 match network_config {
153 NetworkFileConfig::Evm(evm_config) => Ok(Self::new_evm(evm_config)),
154 NetworkFileConfig::Solana(solana_config) => Ok(Self::new_solana(solana_config)),
155 NetworkFileConfig::Stellar(stellar_config) => Ok(Self::new_stellar(stellar_config)),
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 fn create_evm_config(name: &str, chain_id: u64, symbol: &str) -> EvmNetworkConfig {
165 EvmNetworkConfig {
166 common: NetworkConfigCommon {
167 network: name.to_string(),
168 from: None,
169 rpc_urls: Some(vec!["https://rpc.example.com".to_string()]),
170 explorer_urls: Some(vec!["https://explorer.example.com".to_string()]),
171 average_blocktime_ms: Some(12000),
172 is_testnet: Some(false),
173 tags: Some(vec!["mainnet".to_string()]),
174 },
175 chain_id: Some(chain_id),
176 required_confirmations: Some(12),
177 features: Some(vec!["eip1559".to_string()]),
178 symbol: Some(symbol.to_string()),
179 }
180 }
181
182 fn create_solana_config(name: &str, is_testnet: bool) -> SolanaNetworkConfig {
183 SolanaNetworkConfig {
184 common: NetworkConfigCommon {
185 network: name.to_string(),
186 from: None,
187 rpc_urls: Some(vec!["https://api.mainnet-beta.solana.com".to_string()]),
188 explorer_urls: Some(vec!["https://explorer.solana.com".to_string()]),
189 average_blocktime_ms: Some(400),
190 is_testnet: Some(is_testnet),
191 tags: Some(vec!["solana".to_string()]),
192 },
193 }
194 }
195
196 fn create_stellar_config(name: &str, passphrase: Option<&str>) -> StellarNetworkConfig {
197 StellarNetworkConfig {
198 common: NetworkConfigCommon {
199 network: name.to_string(),
200 from: None,
201 rpc_urls: Some(vec!["https://horizon.stellar.org".to_string()]),
202 explorer_urls: Some(vec!["https://stellarchain.io".to_string()]),
203 average_blocktime_ms: Some(5000),
204 is_testnet: Some(passphrase.is_none()),
205 tags: Some(vec!["stellar".to_string()]),
206 },
207 passphrase: passphrase.map(|s| s.to_string()),
208 }
209 }
210
211 #[test]
212 fn test_network_config_data_evm() {
213 let config = create_evm_config("mainnet", 1, "ETH");
214 let config_data = NetworkConfigData::Evm(config);
215
216 assert_eq!(config_data.network_name(), "mainnet");
217 assert_eq!(config_data.network_type(), NetworkType::Evm);
218 assert_eq!(config_data.common().network, "mainnet");
219 assert_eq!(config_data.common().is_testnet, Some(false));
220 }
221
222 #[test]
223 fn test_network_config_data_solana() {
224 let config = create_solana_config("devnet", true);
225 let config_data = NetworkConfigData::Solana(config);
226
227 assert_eq!(config_data.network_name(), "devnet");
228 assert_eq!(config_data.network_type(), NetworkType::Solana);
229 assert_eq!(config_data.common().is_testnet, Some(true));
230 }
231
232 #[test]
233 fn test_network_config_data_stellar() {
234 let config = create_stellar_config("testnet", None);
235 let config_data = NetworkConfigData::Stellar(config);
236
237 assert_eq!(config_data.network_name(), "testnet");
238 assert_eq!(config_data.network_type(), NetworkType::Stellar);
239 assert_eq!(config_data.common().is_testnet, Some(true));
240 }
241
242 #[test]
243 fn test_new_evm() {
244 let config = create_evm_config("mainnet", 1, "ETH");
245 let network_repo = NetworkRepoModel::new_evm(config);
246
247 assert_eq!(network_repo.name, "mainnet");
248 assert_eq!(network_repo.network_type, NetworkType::Evm);
249 assert_eq!(network_repo.id, "evm:mainnet");
250
251 match network_repo.config() {
252 NetworkConfigData::Evm(evm_config) => {
253 assert_eq!(evm_config.chain_id, Some(1));
254 assert_eq!(evm_config.symbol, Some("ETH".to_string()));
255 }
256 _ => panic!("Expected EVM config"),
257 }
258 }
259
260 #[test]
261 fn test_new_solana() {
262 let config = create_solana_config("devnet", true);
263 let network_repo = NetworkRepoModel::new_solana(config);
264
265 assert_eq!(network_repo.name, "devnet");
266 assert_eq!(network_repo.network_type, NetworkType::Solana);
267 assert_eq!(network_repo.id, "solana:devnet");
268
269 match network_repo.config() {
270 NetworkConfigData::Solana(solana_config) => {
271 assert_eq!(solana_config.common.is_testnet, Some(true));
272 }
273 _ => panic!("Expected Solana config"),
274 }
275 }
276
277 #[test]
278 fn test_new_stellar() {
279 let config = create_stellar_config(
280 "mainnet",
281 Some("Public Global Stellar Network ; September 2015"),
282 );
283 let network_repo = NetworkRepoModel::new_stellar(config);
284
285 assert_eq!(network_repo.name, "mainnet");
286 assert_eq!(network_repo.network_type, NetworkType::Stellar);
287 assert_eq!(network_repo.id, "stellar:mainnet");
288
289 match network_repo.config() {
290 NetworkConfigData::Stellar(stellar_config) => {
291 assert_eq!(
292 stellar_config.passphrase,
293 Some("Public Global Stellar Network ; September 2015".to_string())
294 );
295 }
296 _ => panic!("Expected Stellar config"),
297 }
298 }
299
300 #[test]
301 fn test_create_id() {
302 assert_eq!(
303 NetworkRepoModel::create_id(NetworkType::Evm, "Mainnet"),
304 "evm:mainnet"
305 );
306 assert_eq!(
307 NetworkRepoModel::create_id(NetworkType::Solana, "DEVNET"),
308 "solana:devnet"
309 );
310 assert_eq!(
311 NetworkRepoModel::create_id(NetworkType::Stellar, "TestNet"),
312 "stellar:testnet"
313 );
314 }
315
316 #[test]
317 fn test_create_id_with_special_characters() {
318 assert_eq!(
319 NetworkRepoModel::create_id(NetworkType::Evm, "My-Network_123"),
320 "evm:my-network_123"
321 );
322 assert_eq!(
323 NetworkRepoModel::create_id(NetworkType::Solana, "Test Network"),
324 "solana:test network"
325 );
326 }
327
328 #[test]
329 fn test_common_method() {
330 let config = create_evm_config("mainnet", 1, "ETH");
331 let network_repo = NetworkRepoModel::new_evm(config);
332
333 let common = network_repo.common();
334 assert_eq!(common.network, "mainnet");
335 assert_eq!(common.is_testnet, Some(false));
336 assert_eq!(common.average_blocktime_ms, Some(12000));
337 assert_eq!(
338 common.rpc_urls,
339 Some(vec!["https://rpc.example.com".to_string()])
340 );
341 }
342
343 #[test]
344 fn test_config_method() {
345 let config = create_evm_config("mainnet", 1, "ETH");
346 let network_repo = NetworkRepoModel::new_evm(config);
347
348 let config_data = network_repo.config();
349 assert!(matches!(config_data, NetworkConfigData::Evm(_)));
350 assert_eq!(config_data.network_type(), NetworkType::Evm);
351 assert_eq!(config_data.network_name(), "mainnet");
352 }
353
354 #[test]
355 fn test_try_from_evm() {
356 let evm_config = create_evm_config("mainnet", 1, "ETH");
357 let network_file_config = NetworkFileConfig::Evm(evm_config);
358
359 let result = NetworkRepoModel::try_from(network_file_config);
360 assert!(result.is_ok());
361
362 let network_repo = result.unwrap();
363 assert_eq!(network_repo.name, "mainnet");
364 assert_eq!(network_repo.network_type, NetworkType::Evm);
365 assert_eq!(network_repo.id, "evm:mainnet");
366 }
367
368 #[test]
369 fn test_try_from_solana() {
370 let solana_config = create_solana_config("devnet", true);
371 let network_file_config = NetworkFileConfig::Solana(solana_config);
372
373 let result = NetworkRepoModel::try_from(network_file_config);
374 assert!(result.is_ok());
375
376 let network_repo = result.unwrap();
377 assert_eq!(network_repo.name, "devnet");
378 assert_eq!(network_repo.network_type, NetworkType::Solana);
379 assert_eq!(network_repo.id, "solana:devnet");
380 }
381
382 #[test]
383 fn test_try_from_stellar() {
384 let stellar_config = create_stellar_config("testnet", None);
385 let network_file_config = NetworkFileConfig::Stellar(stellar_config);
386
387 let result = NetworkRepoModel::try_from(network_file_config);
388 assert!(result.is_ok());
389
390 let network_repo = result.unwrap();
391 assert_eq!(network_repo.name, "testnet");
392 assert_eq!(network_repo.network_type, NetworkType::Stellar);
393 assert_eq!(network_repo.id, "stellar:testnet");
394 }
395
396 #[test]
397 fn test_serialization_roundtrip() {
398 let config = create_evm_config("mainnet", 1, "ETH");
399 let network_repo = NetworkRepoModel::new_evm(config);
400
401 let serialized = serde_json::to_string(&network_repo).unwrap();
402 let deserialized: NetworkRepoModel = serde_json::from_str(&serialized).unwrap();
403
404 assert_eq!(network_repo.id, deserialized.id);
405 assert_eq!(network_repo.name, deserialized.name);
406 assert_eq!(network_repo.network_type, deserialized.network_type);
407 }
408
409 #[test]
410 fn test_clone() {
411 let config = create_evm_config("mainnet", 1, "ETH");
412 let network_repo = NetworkRepoModel::new_evm(config);
413 let cloned = network_repo.clone();
414
415 assert_eq!(network_repo.id, cloned.id);
416 assert_eq!(network_repo.name, cloned.name);
417 assert_eq!(network_repo.network_type, cloned.network_type);
418 }
419
420 #[test]
421 fn test_debug() {
422 let config = create_evm_config("mainnet", 1, "ETH");
423 let network_repo = NetworkRepoModel::new_evm(config);
424
425 let debug_str = format!("{:?}", network_repo);
426 assert!(debug_str.contains("NetworkRepoModel"));
427 assert!(debug_str.contains("mainnet"));
428 assert!(debug_str.contains("Evm"));
429 }
430
431 #[test]
432 fn test_network_types_consistency() {
433 let evm_config = create_evm_config("mainnet", 1, "ETH");
434 let solana_config = create_solana_config("devnet", true);
435 let stellar_config = create_stellar_config("testnet", None);
436
437 let evm_repo = NetworkRepoModel::new_evm(evm_config);
438 let solana_repo = NetworkRepoModel::new_solana(solana_config);
439 let stellar_repo = NetworkRepoModel::new_stellar(stellar_config);
440
441 assert_eq!(evm_repo.network_type, evm_repo.config().network_type());
442 assert_eq!(
443 solana_repo.network_type,
444 solana_repo.config().network_type()
445 );
446 assert_eq!(
447 stellar_repo.network_type,
448 stellar_repo.config().network_type()
449 );
450 }
451
452 #[test]
453 fn test_empty_optional_fields() {
454 let minimal_config = EvmNetworkConfig {
455 common: NetworkConfigCommon {
456 network: "minimal".to_string(),
457 from: None,
458 rpc_urls: Some(vec!["https://rpc.example.com".to_string()]),
459 explorer_urls: None,
460 average_blocktime_ms: None,
461 is_testnet: None,
462 tags: None,
463 },
464 chain_id: Some(1),
465 required_confirmations: Some(1),
466 features: None,
467 symbol: Some("ETH".to_string()),
468 };
469
470 let network_repo = NetworkRepoModel::new_evm(minimal_config);
471 assert_eq!(network_repo.name, "minimal");
472 assert_eq!(network_repo.common().explorer_urls, None);
473 assert_eq!(network_repo.common().tags, None);
474 }
475}