Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: liquid standard templates #231

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ repository = "https://github.com/r0gue-io/pop-cli"
[workspace.dependencies]
anyhow = "1.0"
assert_cmd = "2.0.14"
cargo-generate = "0.21.1"
dirs = "5.0"
duct = "0.13"
env_logger = "0.11.1"
Expand Down Expand Up @@ -68,4 +69,4 @@ cliclack = "0.3.1"
console = "0.15"
os_info = { version = "3", default-features = false }
strum = "0.26"
strum_macros = "0.26"
strum_macros = "0.26"
12 changes: 2 additions & 10 deletions crates/pop-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,6 @@ predicates.workspace = true

[features]
default = ["contract", "parachain", "telemetry"]
contract = [
"dep:pop-contracts",
"dep:sp-core",
"dep:sp-weights",
"dep:dirs",
]
parachain = [
"dep:pop-parachains",
"dep:dirs",
]
contract = ["dep:pop-contracts", "dep:sp-core", "dep:sp-weights", "dep:dirs"]
parachain = ["dep:pop-parachains", "dep:dirs"]
telemetry = ["dep:pop-telemetry"]
33 changes: 23 additions & 10 deletions crates/pop-cli/src/commands/new/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ use cliclack::{
};
use pop_common::{Git, GitHub, Release};
use pop_parachains::{
instantiate_template_dir, is_initial_endowment_valid, Config, Provider, Template,
instantiate_template_dir, is_decimals_valid, is_initial_endowment_valid, Config, Provider,
Template,
};
use strum::VariantArray;

const DEFAULT_DECIMALS: &str = "12";
const DEFAULT_INITIAL_ENDOWMENT: &str = "1u64 << 60";

#[derive(Args, Clone)]
Expand Down Expand Up @@ -46,8 +48,8 @@ pub struct NewParachainCommand {
pub(crate) release_tag: Option<String>,
#[arg(long, short, help = "Token Symbol", default_value = "UNIT")]
pub(crate) symbol: Option<String>,
#[arg(long, short, help = "Token Decimals", default_value = "12")]
pub(crate) decimals: Option<u8>,
#[arg(long, short, help = "Token Decimals", default_value = DEFAULT_DECIMALS)]
pub(crate) decimals: Option<String>,
#[arg(
long = "endowment",
short,
Expand Down Expand Up @@ -126,7 +128,7 @@ async fn guide_user_to_generate_parachain() -> Result<NewParachainCommand> {

let mut customizable_options = Config {
symbol: "UNIT".to_string(),
decimals: 12,
decimals: "12".to_string(),
initial_endowment: "1u64 << 60".to_string(),
};
if template.matches(&Provider::Pop) {
Expand Down Expand Up @@ -235,7 +237,7 @@ fn display_select_options(provider: &Provider) -> Result<&Template> {
fn get_customization_value(
template: &Template,
symbol: Option<String>,
decimals: Option<u8>,
decimals: Option<String>,
initial_endowment: Option<String>,
) -> Result<Config> {
if !matches!(template, Template::Standard)
Expand Down Expand Up @@ -344,11 +346,22 @@ fn prompt_customizable_options() -> Result<Config> {
.default_input("UNIT")
.interact()?;

let decimals_input: String = input("How many token decimals?")
let mut decimals: String = input("How many token decimals?")
.placeholder("12")
.default_input("12")
.interact()?;
let decimals: u8 = decimals_input.parse::<u8>().expect("input has to be a number");
if !is_decimals_valid(&decimals) {
outro_cancel("⚠️ The specified decimals is not valid, input has to be numeric")?;
//Prompt the user if want to use the one by default
if !confirm(format!("📦 Would you like to use the default {}?", DEFAULT_DECIMALS))
.initial_value(true)
.interact()?
{
outro_cancel("🚫 Cannot create a parachain with an incorrect decimals value.")?;
return Err(anyhow::anyhow!("incorrect decimals value"));
}
decimals = DEFAULT_DECIMALS.to_string();
}

let mut initial_endowment: String = input("And the initial endowment for dev accounts?")
.placeholder("1u64 << 60")
Expand Down Expand Up @@ -417,7 +430,7 @@ mod tests {
template: Some(Template::Standard),
release_tag: None,
symbol: Some("UNIT".to_string()),
decimals: Some(12),
decimals: Some("12".to_string()),
initial_endowment: Some("1u64 << 60".to_string()),
};
command.execute().await?;
Expand Down Expand Up @@ -446,14 +459,14 @@ mod tests {
let config = get_customization_value(
&Template::Standard,
Some("DOT".to_string()),
Some(6),
Some("6".to_string()),
Some("10000".to_string()),
)?;
assert_eq!(
config,
Config {
symbol: "DOT".to_string(),
decimals: 6,
decimals: "6".to_string(),
initial_endowment: "10000".to_string()
}
);
Expand Down
1 change: 1 addition & 0 deletions crates/pop-parachains/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ repository.workspace = true

[dependencies]
anyhow.workspace = true
cargo-generate.workspace = true
duct.workspace = true
flate2.workspace = true
glob.workspace = true
Expand Down
1 change: 0 additions & 1 deletion crates/pop-parachains/src/generator/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-3.0

pub mod pallet;
pub mod parachain;
17 changes: 0 additions & 17 deletions crates/pop-parachains/src/generator/parachain.rs

This file was deleted.

2 changes: 1 addition & 1 deletion crates/pop-parachains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub use new_pallet::{create_pallet_template, TemplatePalletConfig};
pub use new_parachain::instantiate_template_dir;
pub use templates::{Config, Provider, Template};
pub use up::{Binary, Status, Zombienet};
pub use utils::helpers::is_initial_endowment_valid;
pub use utils::helpers::{is_decimals_valid, is_initial_endowment_valid};
pub use utils::pallet_helpers::resolve_pallet_path;
/// Information about the Node. External export from Zombienet-SDK.
pub use zombienet_sdk::NetworkNode;
Expand Down
95 changes: 54 additions & 41 deletions crates/pop-parachains/src/new_parachain.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
// SPDX-License-Identifier: GPL-3.0

use crate::{
generator::parachain::{ChainSpec, Network},
utils::helpers::{sanitize, write_to_file},
Config, Provider, Template,
};
use crate::{utils::helpers::sanitize, Config, Provider, Template};
use anyhow::Result;
use cargo_generate::{GenerateArgs, TemplatePath};
use pop_common::git::Git;
use std::{fs, path::Path};
use walkdir::WalkDir;

/// Create a new parachain.
///
Expand Down Expand Up @@ -39,37 +35,50 @@ pub fn instantiate_standard_template(
config: Config,
tag_version: Option<String>,
) -> Result<Option<String>> {
let temp_dir = ::tempfile::TempDir::new_in(std::env::temp_dir())?;
let source = temp_dir.path();
// Template palceholder definitions
al3mart marked this conversation as resolved.
Show resolved Hide resolved
let mut token_symbol: String = "token-symbol=".to_string();
let mut token_decimals: String = "token-decimals=".to_string();
let mut initial_endowement: String = "initial-endowment=".to_string();

let tag = Git::clone_and_degit(template.repository_url()?, source, tag_version)?;
// Placeholder customization
token_symbol.push_str(&*config.symbol);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just small comment, this:

let mut token_symbol: String = "token-symbol=".to_string();
token_symbol.push_str(&*config.symbol);

can be refactor to:

let token_symbol: String = format!("token-symbol={}", config.symbol);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this makes more sense!

token_decimals.push_str(&*config.decimals);
initial_endowement.push_str(&*config.initial_endowment);

for entry in WalkDir::new(&source) {
let entry = entry?;
// Tempalte rendering arguments
al3mart marked this conversation as resolved.
Show resolved Hide resolved
let standard_template_path = TemplatePath {
git: Some(
template
.repository_url()
.map_or(String::from("https://github.com/r0gue.io/base-parachain"), |r| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template.repository_url() should never produce an error because it is our responsibility to set the repository URL correctly.
Not sure the best approach here, personally I'd throw an error instead of hardcoding the base-parachain url, or if we don't want to throw an error return a the DEFAULT value in the repository_url function(https://github.com/r0gue-io/pop-cli/blob/main/crates/pop-parachains/src/templates.rs#L225, )

r.to_string()
}),
),
tag: tag_version.clone(),
..Default::default()
};

let template_generation_args = GenerateArgs {
template_path: standard_template_path,
name: Some(target.file_name().unwrap().to_str().unwrap().to_string()),
destination: Some(target.parent().unwrap().to_path_buf()),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type conversions here could maybe be done in a cleaner way ?

define: vec![token_symbol, token_decimals, initial_endowement],
..Default::default()
};

let source_path = entry.path();
let destination_path = target.join(source_path.strip_prefix(&source)?);
// Template rendering
let target_template_path = cargo_generate::generate(template_generation_args)
.expect("Couldn't render liquid tempalte");
al3mart marked this conversation as resolved.
Show resolved Hide resolved

if entry.file_type().is_dir() {
fs::create_dir_all(&destination_path)?;
} else {
fs::copy(source_path, &destination_path)?;
}
// Degit
let target_dirs = vec![".git", ".github"];
let remove_target = Path::new(&target_template_path);
for dir in target_dirs {
let git_dir = Path::new(dir);
fs::remove_dir_all(&remove_target.join(git_dir))?;
}
let chainspec = ChainSpec {
token_symbol: config.symbol,
decimals: config.decimals,
initial_endowment: config.initial_endowment,
};
use askama::Template;
write_to_file(
&target.join("node/src/chain_spec.rs"),
chainspec.render().expect("infallible").as_ref(),
)?;
// Add network configuration
let network = Network { node: "parachain-template-node".into() };
write_to_file(&target.join("network.toml"), network.render().expect("infallible").as_ref())?;
Ok(tag)

Ok(tag_version)
}

#[cfg(test)]
Expand All @@ -80,12 +89,19 @@ mod tests {

fn setup_template_and_instantiate() -> Result<tempfile::TempDir> {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
println!("{:?}", temp_dir);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

let config = Config {
symbol: "DOT".to_string(),
decimals: 18,
decimals: "18".to_string(),
initial_endowment: "1000000".to_string(),
};
instantiate_standard_template(&Template::Standard, temp_dir.path(), config, None)?;
let _ = sanitize(temp_dir.path());
instantiate_standard_template(
&Template::Standard,
temp_dir.path(),
config,
Some(String::from("liquid-template")),
)?;
Ok(temp_dir)
}

Expand All @@ -107,13 +123,10 @@ mod tests {
// Verify network.toml contains expected content
let generated_file_content =
fs::read_to_string(temp_dir.path().join("network.toml")).expect("Failed to read file");
let expected_file_content =
fs::read_to_string(current_dir()?.join("./templates/base/network.templ"))
.expect("Failed to read file");
assert_eq!(
generated_file_content,
expected_file_content.replace("^^node^^", "parachain-template-node")
);
let mut expected_file_content =
temp_dir.path().file_name().unwrap().to_str().unwrap().to_string().to_owned();
expected_file_content.push_str("-node");
assert!(generated_file_content.contains(&expected_file_content));

Ok(())
}
Expand Down
8 changes: 6 additions & 2 deletions crates/pop-parachains/src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl Provider {
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
pub symbol: String,
pub decimals: u8,
pub decimals: String,
pub initial_endowment: String,
}

Expand Down Expand Up @@ -100,7 +100,8 @@ pub enum Template {
props(
Provider = "Pop",
Repository = "https://github.com/r0gue-io/base-parachain",
Network = "./network.toml"
Network = "./network.toml",
SupportedVersions = "liquid-template"
)
)]
Standard,
Expand Down Expand Up @@ -236,15 +237,18 @@ impl Template {
self.get_str("Network")
}

/// Get a list of the template' supported versions.
pub fn supported_versions(&self) -> Option<Vec<&str>> {
self.get_str("SupportedVersions").map(|s| s.split(',').collect())
}

/// Check if the provided version is supported.
pub fn is_supported_version(&self, version: &str) -> bool {
// if `SupportedVersion` is None, then all versions are supported. Otherwise, ensure version is present.
self.supported_versions().map_or(true, |versions| versions.contains(&version))
}

/// Check if the template has been audited.
pub fn is_audited(&self) -> bool {
self.get_str("IsAudited").map_or(false, |s| s == "true")
}
Expand Down
36 changes: 10 additions & 26 deletions crates/pop-parachains/src/utils/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ pub(crate) fn sanitize(target: &Path) -> Result<(), Error> {
Ok(())
}

/// Check if the decimals input by the user is a valid numeric value.
///
/// # Arguments
///
/// * `decimals` - decimals input to be checked for validity.
pub fn is_decimals_valid(decimals: &str) -> bool {
al3mart marked this conversation as resolved.
Show resolved Hide resolved
decimals.chars().all(char::is_numeric)
}

/// Check if the initial endowment input by the user is a valid balance.
///
/// # Arguments
Expand All @@ -33,6 +42,7 @@ pub fn is_initial_endowment_valid(initial_endowment: &str) -> bool {
initial_endowment.parse::<u128>().is_ok()
|| is_valid_bitwise_left_shift(initial_endowment).is_ok()
}

// Auxiliar method to check if the endowment input with a shift left (1u64 << 60) format is valid.
// Parse the self << rhs format and check the shift left operation is valid.
fn is_valid_bitwise_left_shift(initial_endowment: &str) -> Result<u128, Error> {
Expand Down Expand Up @@ -85,32 +95,6 @@ pub(crate) fn write_to_file(path: &Path, contents: &str) -> Result<(), Error> {
#[cfg(test)]
mod tests {
use super::*;
use crate::generator::parachain::ChainSpec;
use askama::Template;
use tempfile::tempdir;

#[test]
fn test_write_to_file() -> Result<(), Box<dyn std::error::Error>> {
Copy link
Contributor Author

@al3mart al3mart Jun 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't do this anymore, hence the removal.

let temp_dir = tempdir()?;
let chainspec = ChainSpec {
token_symbol: "DOT".to_string(),
decimals: 6,
initial_endowment: "1000000".to_string(),
};
let file_path = temp_dir.path().join("file.rs");
let _ = fs::write(&file_path, "");
write_to_file(&file_path, chainspec.render().expect("infallible").as_ref())?;
let generated_file_content =
fs::read_to_string(temp_dir.path().join("file.rs")).expect("Failed to read file");

assert!(generated_file_content
.contains("properties.insert(\"tokenSymbol\".into(), \"DOT\".into());"));
assert!(generated_file_content
.contains("properties.insert(\"tokenDecimals\".into(), 6.into());"));
assert!(generated_file_content.contains("1000000"));

Ok(())
}

#[test]
fn test_is_initial_endowment_valid() {
Expand Down
Loading