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