1use pop_common::templates::{Template, Type};
10use strum::{EnumProperty as _, VariantArray};
11use strum_macros::{AsRefStr, Display, EnumMessage, EnumProperty, EnumString};
12
13#[derive(
15 AsRefStr, Clone, Default, Debug, Display, EnumMessage, EnumString, Eq, PartialEq, VariantArray,
16)]
17pub enum Provider {
18 #[default]
20 #[strum(
21 ascii_case_insensitive,
22 serialize = "pop",
23 message = "Pop",
24 detailed_message = "An all-in-one tool for Polkadot development."
25 )]
26 Pop,
27 #[strum(
29 ascii_case_insensitive,
30 serialize = "openzeppelin",
31 message = "OpenZeppelin",
32 detailed_message = "The standard for secure blockchain applications."
33 )]
34 OpenZeppelin,
35 #[strum(
37 ascii_case_insensitive,
38 serialize = "parity",
39 message = "Parity",
40 detailed_message = "Solutions for a trust-free world."
41 )]
42 Parity,
43}
44
45impl Type<ChainTemplate> for Provider {
46 fn default_template(&self) -> Option<ChainTemplate> {
47 match &self {
48 Provider::Pop => Some(ChainTemplate::Standard),
49 Provider::OpenZeppelin => Some(ChainTemplate::OpenZeppelinGeneric),
50 Provider::Parity => Some(ChainTemplate::ParityGeneric),
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq)]
57pub struct Config {
58 pub symbol: String,
60 pub decimals: u8,
62 pub initial_endowment: String,
64}
65
66#[derive(
68 AsRefStr,
69 Clone,
70 Debug,
71 Default,
72 Display,
73 EnumMessage,
74 EnumProperty,
75 EnumString,
76 Eq,
77 Hash,
78 PartialEq,
79 VariantArray,
80)]
81pub enum ChainTemplate {
82 #[default]
84 #[strum(
85 serialize = "r0gue-io/base-parachain",
86 message = "Standard",
87 detailed_message = "A standard parachain",
88 props(
89 Provider = "Pop",
90 Repository = "https://github.com/r0gue-io/base-parachain",
91 Network = "./network.toml",
92 License = "Unlicense",
93 DeploymentName = "POP_STANDARD"
94 )
95 )]
96 Standard,
97 #[strum(
100 serialize = "r0gue-io/assets-parachain",
101 message = "Assets",
102 detailed_message = "Parachain configured with fungible and non-fungible asset functionalities.",
103 props(
104 Provider = "Pop",
105 Repository = "https://github.com/r0gue-io/assets-parachain",
106 Network = "./network.toml",
107 License = "Unlicense",
108 DeploymentName = "POP_ASSETS"
109 )
110 )]
111 Assets,
112 #[strum(
114 serialize = "r0gue-io/contracts-parachain",
115 message = "Contracts",
116 detailed_message = "Parachain configured to support WebAssembly smart contracts.",
117 props(
118 Provider = "Pop",
119 Repository = "https://github.com/r0gue-io/contracts-parachain",
120 Network = "./network.toml",
121 License = "Unlicense",
122 DeploymentName = "POP_CONTRACTS"
123 )
124 )]
125 Contracts,
126 #[strum(
128 serialize = "openzeppelin/generic-template",
129 message = "Generic Runtime Template",
130 detailed_message = "A generic template for Substrate Runtime.",
131 props(
132 Provider = "OpenZeppelin",
133 Repository = "https://github.com/OpenZeppelin/polkadot-runtime-templates",
134 Network = "./zombienet-config/devnet.toml",
135 SupportedVersions = "v1.0.0,v2.0.1,v2.0.3,v3.0.0,v4.0.0",
136 IsAudited = "true",
137 License = "GPL-3.0",
138 DeploymentName = "OZ_GENERIC"
139 )
140 )]
141 OpenZeppelinGeneric,
142 #[strum(
144 serialize = "openzeppelin/evm-template",
145 message = "EVM Template",
146 detailed_message = "Parachain with EVM compatibility out of the box.",
147 props(
148 Provider = "OpenZeppelin",
149 Repository = "https://github.com/OpenZeppelin/polkadot-runtime-templates",
150 Network = "./zombienet-config/devnet.toml",
151 SupportedVersions = "v2.0.3,v3.0.0,v4.0.0",
152 IsAudited = "true",
153 License = "GPL-3.0",
154 DeploymentName = "OZ_EVM"
155 )
156 )]
157 OpenZeppelinEVM,
158 #[strum(
160 serialize = "paritytech/polkadot-sdk-parachain-template",
161 message = "Polkadot SDK's Parachain Template",
162 detailed_message = "The Parachain-Ready Template From Polkadot SDK.",
163 props(
164 Provider = "Parity",
165 Repository = "https://github.com/paritytech/polkadot-sdk-parachain-template",
166 Network = "./zombienet.toml",
167 License = "Unlicense",
168 DeploymentName = "PARITY_GENERIC"
169 )
170 )]
171 ParityGeneric,
172 #[cfg(test)]
174 #[strum(
175 serialize = "test_01",
176 message = "Test_01",
177 detailed_message = "Test template only compiled in test mode.",
178 props(
179 Provider = "Test",
180 Repository = "",
181 Network = "",
182 SupportedVersions = "v1.0.0,v2.0.0",
183 IsAudited = "true",
184 IsDeprecated = "true",
185 DeprecatedMessage = "This template is deprecated. Please use test_02 in the future.",
186 License = "Unlicense",
187 )
188 )]
189 TestTemplate01,
190 #[cfg(test)]
192 #[strum(
193 serialize = "test_02",
194 message = "Test_02",
195 detailed_message = "Test template only compiled in test mode.",
196 props(Provider = "Test", Repository = "", Network = "", License = "GPL-3.0")
197 )]
198 TestTemplate02,
199}
200
201impl Template for ChainTemplate {
202 const PROPERTY: &'static str = "Provider";
203}
204
205impl ChainTemplate {
206 pub fn network_config(&self) -> Option<&str> {
208 self.get_str("Network")
209 }
210
211 pub fn supported_versions(&self) -> Option<Vec<&str>> {
213 self.get_str("SupportedVersions").map(|s| s.split(',').collect())
214 }
215
216 pub fn is_supported_version(&self, version: &str) -> bool {
221 self.supported_versions().is_none_or(|versions| versions.contains(&version))
224 }
225
226 pub fn is_audited(&self) -> bool {
228 self.get_str("IsAudited") == Some("true")
229 }
230
231 pub fn license(&self) -> Option<&str> {
233 self.get_str("License")
234 }
235
236 pub fn deployment_name(&self) -> Option<&str> {
238 self.get_str("DeploymentName")
239 }
240
241 pub fn deployment_name_from_based_on(based_on: &str) -> Option<String> {
243 let mapped_based_on = match based_on {
245 "OpenZeppelin EVM Template" => Some(ChainTemplate::OpenZeppelinEVM),
246 "OpenZeppelin Generic Template" => Some(ChainTemplate::OpenZeppelinGeneric),
247 _ => None,
248 };
249 if let Some(variant) = mapped_based_on {
250 return variant.deployment_name().map(String::from);
251 }
252 ChainTemplate::VARIANTS
253 .iter()
254 .find(|variant| variant.as_ref() == based_on)
255 .and_then(|variant| variant.deployment_name().map(String::from))
256 }
257
258 pub fn template_name_without_provider(&self) -> &str {
260 let name = self.as_ref();
261 name.split_once('/').map_or(name, |(_, template)| template)
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268 use ChainTemplate::*;
269 use std::{collections::HashMap, str::FromStr};
270
271 fn templates_names() -> HashMap<String, ChainTemplate> {
272 HashMap::from([
273 ("r0gue-io/base-parachain".to_string(), Standard),
274 ("r0gue-io/assets-parachain".to_string(), Assets),
275 ("r0gue-io/contracts-parachain".to_string(), Contracts),
276 ("openzeppelin/generic-template".to_string(), OpenZeppelinGeneric),
278 ("openzeppelin/evm-template".to_string(), OpenZeppelinEVM),
279 ("paritytech/polkadot-sdk-parachain-template".to_string(), ParityGeneric),
281 ("test_01".to_string(), TestTemplate01),
282 ("test_02".to_string(), TestTemplate02),
283 ])
284 }
285
286 fn templates_names_without_providers() -> HashMap<ChainTemplate, String> {
287 HashMap::from([
288 (Standard, "base-parachain".to_string()),
289 (Assets, "assets-parachain".to_string()),
290 (Contracts, "contracts-parachain".to_string()),
291 (OpenZeppelinGeneric, "generic-template".to_string()),
292 (OpenZeppelinEVM, "evm-template".to_string()),
293 (ParityGeneric, "polkadot-sdk-parachain-template".to_string()),
294 (TestTemplate01, "test_01".to_string()),
295 (TestTemplate02, "test_02".to_string()),
296 ])
297 }
298
299 fn templates_urls() -> HashMap<String, &'static str> {
300 HashMap::from([
301 ("r0gue-io/base-parachain".to_string(), "https://github.com/r0gue-io/base-parachain"),
302 (
303 "r0gue-io/assets-parachain".to_string(),
304 "https://github.com/r0gue-io/assets-parachain",
305 ),
306 (
307 "r0gue-io/contracts-parachain".to_string(),
308 "https://github.com/r0gue-io/contracts-parachain",
309 ),
310 ("r0gue-io/evm-parachain".to_string(), "https://github.com/r0gue-io/evm-parachain"),
311 (
313 "openzeppelin/generic-template".to_string(),
314 "https://github.com/OpenZeppelin/polkadot-runtime-templates",
315 ),
316 (
317 "openzeppelin/evm-template".to_string(),
318 "https://github.com/OpenZeppelin/polkadot-runtime-templates",
319 ),
320 (
321 "polkadot-generic-runtime-template".to_string(),
322 "https://github.com/OpenZeppelin/polkadot-runtime-templates",
323 ),
324 (
325 "paritytech/polkadot-sdk-parachain-template".to_string(),
326 "https://github.com/paritytech/polkadot-sdk-parachain-template",
327 ),
328 (
329 "paritytech/substrate-contracts-node".to_string(),
330 "https://github.com/paritytech/substrate-contracts-node",
331 ),
332 ("cpt".to_string(), "https://github.com/paritytech/substrate-contracts-node"),
333 ("test_01".to_string(), ""),
334 ("test_02".to_string(), ""),
335 ])
336 }
337
338 fn template_network_configs() -> HashMap<ChainTemplate, Option<&'static str>> {
339 [
340 (Standard, Some("./network.toml")),
341 (Assets, Some("./network.toml")),
342 (Contracts, Some("./network.toml")),
343 (OpenZeppelinGeneric, Some("./zombienet-config/devnet.toml")),
344 (OpenZeppelinEVM, Some("./zombienet-config/devnet.toml")),
345 (ParityGeneric, Some("./zombienet.toml")),
346 (TestTemplate01, Some("")),
347 (TestTemplate02, Some("")),
348 ]
349 .into()
350 }
351
352 fn template_license() -> HashMap<ChainTemplate, Option<&'static str>> {
353 [
354 (Standard, Some("Unlicense")),
355 (Assets, Some("Unlicense")),
356 (Contracts, Some("Unlicense")),
357 (OpenZeppelinGeneric, Some("GPL-3.0")),
358 (OpenZeppelinEVM, Some("GPL-3.0")),
359 (ParityGeneric, Some("Unlicense")),
360 (TestTemplate01, Some("Unlicense")),
361 (TestTemplate02, Some("GPL-3.0")),
362 ]
363 .into()
364 }
365
366 fn template_deployment_name() -> HashMap<ChainTemplate, Option<&'static str>> {
367 [
368 (Standard, Some("POP_STANDARD")),
369 (Assets, Some("POP_ASSETS")),
370 (Contracts, Some("POP_CONTRACTS")),
371 (OpenZeppelinGeneric, Some("OZ_GENERIC")),
372 (OpenZeppelinEVM, Some("OZ_EVM")),
373 (ParityGeneric, Some("PARITY_GENERIC")),
374 (TestTemplate01, None),
375 (TestTemplate02, None),
376 ]
377 .into()
378 }
379
380 #[test]
381 fn test_is_template_correct() {
382 for template in ChainTemplate::VARIANTS {
383 if matches!(template, Standard | Assets | Contracts) {
384 assert!(Provider::Pop.provides(template));
385 assert!(!Provider::Parity.provides(template));
386 }
387 if matches!(template, ParityGeneric) {
388 assert!(!Provider::Pop.provides(template));
389 assert!(Provider::Parity.provides(template))
390 }
391 }
392 }
393
394 #[test]
395 fn test_convert_string_to_template() {
396 let template_names = templates_names();
397 assert_eq!(ChainTemplate::from_str("").unwrap_or_default(), Standard);
399 for template in ChainTemplate::VARIANTS {
401 assert_eq!(
402 &ChainTemplate::from_str(template.as_ref()).unwrap(),
403 template_names.get(&template.to_string()).unwrap()
404 );
405 }
406 }
407
408 #[test]
409 fn test_repository_url() {
410 let template_urls = templates_urls();
411 for template in ChainTemplate::VARIANTS {
412 assert_eq!(
413 &template.repository_url().unwrap(),
414 template_urls.get(&template.to_string()).unwrap()
415 );
416 }
417 }
418
419 #[test]
420 fn test_network_config() {
421 let network_configs = template_network_configs();
422 for template in ChainTemplate::VARIANTS {
423 println!("{:?}", template.name());
424 assert_eq!(template.network_config(), network_configs[template]);
425 }
426 }
427
428 #[test]
429 fn test_license() {
430 let licenses = template_license();
431 for template in ChainTemplate::VARIANTS {
432 assert_eq!(template.license(), licenses[template]);
433 }
434 }
435
436 #[test]
437 fn deployment_name_works() {
438 let deployment_name = template_deployment_name();
439 for template in ChainTemplate::VARIANTS {
440 assert_eq!(template.deployment_name(), deployment_name[template]);
441 }
442 }
443
444 #[test]
445 fn deployment_name_from_based_on_works() {
446 for template in ChainTemplate::VARIANTS {
447 assert_eq!(
448 ChainTemplate::deployment_name_from_based_on(template.as_ref()),
449 template.deployment_name().map(String::from),
450 );
451 }
452 assert_eq!(
454 ChainTemplate::deployment_name_from_based_on("OpenZeppelin EVM Template"),
455 Some(OpenZeppelinEVM.deployment_name().unwrap().to_string())
456 );
457 assert_eq!(
458 ChainTemplate::deployment_name_from_based_on("OpenZeppelin Generic Template"),
459 Some(OpenZeppelinGeneric.deployment_name().unwrap().to_string())
460 );
461 }
462
463 #[test]
464 fn test_default_template_of_provider() {
465 let mut provider = Provider::Pop;
466 assert_eq!(provider.default_template(), Some(Standard));
467 provider = Provider::Parity;
468 assert_eq!(provider.default_template(), Some(ParityGeneric));
469 }
470
471 #[test]
472 fn test_templates_of_provider() {
473 let mut provider = Provider::Pop;
474 assert_eq!(provider.templates(), [&Standard, &Assets, &Contracts]);
475 provider = Provider::Parity;
476 assert_eq!(provider.templates(), [&ParityGeneric]);
477 }
478
479 #[test]
480 fn test_convert_string_to_provider() {
481 assert_eq!(Provider::from_str("Pop").unwrap(), Provider::Pop);
482 assert_eq!(Provider::from_str("").unwrap_or_default(), Provider::Pop);
483 assert_eq!(Provider::from_str("Parity").unwrap(), Provider::Parity);
484 }
485
486 #[test]
487 fn supported_versions_have_no_whitespace() {
488 for template in ChainTemplate::VARIANTS {
489 if let Some(versions) = template.supported_versions() {
490 for version in versions {
491 assert!(!version.contains(' '));
492 }
493 }
494 }
495 }
496
497 #[test]
498 fn test_supported_versions_works() {
499 let template = TestTemplate01;
500 assert_eq!(template.supported_versions(), Some(vec!["v1.0.0", "v2.0.0"]));
501 assert!(template.is_supported_version("v1.0.0"));
502 assert!(template.is_supported_version("v2.0.0"));
503 assert!(!template.is_supported_version("v3.0.0"));
504
505 let template = TestTemplate02;
506 assert_eq!(template.supported_versions(), None);
507 assert!(template.is_supported_version("v1.0.0"));
509 }
510
511 #[test]
512 fn test_is_audited() {
513 let template = TestTemplate01;
514 assert!(template.is_audited());
515
516 let template = TestTemplate02;
517 assert!(!template.is_audited());
518 }
519
520 #[test]
521 fn is_deprecated_works() {
522 let template = TestTemplate01;
523 assert!(template.is_deprecated());
524
525 let template = TestTemplate02;
526 assert!(!template.is_deprecated());
527 }
528
529 #[test]
530 fn deprecated_message_works() {
531 let template = TestTemplate01;
532 assert_eq!(
533 template.deprecated_message(),
534 "This template is deprecated. Please use test_02 in the future."
535 );
536
537 let template = TestTemplate02;
538 assert_eq!(template.deprecated_message(), "");
539 }
540
541 #[test]
542 fn template_name_without_provider() {
543 let template_names = templates_names_without_providers();
544 for template in ChainTemplate::VARIANTS {
545 assert_eq!(
546 template.template_name_without_provider(),
547 template_names.get(template).unwrap()
548 );
549 }
550 }
551}