diff --git a/zk_toolbox/README.md b/zk_toolbox/README.md index b35d4c8d56f1..469e36a65f64 100644 --- a/zk_toolbox/README.md +++ b/zk_toolbox/README.md @@ -320,6 +320,14 @@ Create a snapshot of the current chain: zks snapshot create ``` +### Contracts + +Build contracts: + +```bash +zks contracts +``` + ### Format Format code: diff --git a/zk_toolbox/crates/zk_supervisor/src/commands/contracts.rs b/zk_toolbox/crates/zk_supervisor/src/commands/contracts.rs new file mode 100644 index 000000000000..0c635b2b0d34 --- /dev/null +++ b/zk_toolbox/crates/zk_supervisor/src/commands/contracts.rs @@ -0,0 +1,135 @@ +use std::path::PathBuf; + +use clap::{Parser, ValueEnum}; +use common::{cmd::Cmd, logger, spinner::Spinner}; +use config::EcosystemConfig; +use strum::EnumIter; +use xshell::{cmd, Shell}; + +use crate::messages::{ + MSG_BUILDING_CONTRACTS, MSG_BUILDING_CONTRACTS_SUCCESS, MSG_BUILDING_L1_CONTRACTS_SPINNER, + MSG_BUILDING_L2_CONTRACTS_SPINNER, MSG_BUILDING_SYSTEM_CONTRACTS_SPINNER, + MSG_BUILD_L1_CONTRACTS_HELP, MSG_BUILD_L2_CONTRACTS_HELP, MSG_BUILD_SYSTEM_CONTRACTS_HELP, + MSG_CONTRACTS_DEPS_SPINNER, MSG_NOTHING_TO_BUILD_MSG, +}; + +#[derive(Debug, Parser)] +pub struct ContractsArgs { + #[clap(long, alias = "l1", help = MSG_BUILD_L1_CONTRACTS_HELP, default_missing_value = "true", num_args = 0..=1)] + pub l1_contracts: Option, + #[clap(long, alias = "l2", help = MSG_BUILD_L2_CONTRACTS_HELP, default_missing_value = "true", num_args = 0..=1)] + pub l2_contracts: Option, + #[clap(long, alias = "sc", help = MSG_BUILD_SYSTEM_CONTRACTS_HELP, default_missing_value = "true", num_args = 0..=1)] + pub system_contracts: Option, +} + +impl ContractsArgs { + fn contracts(&self) -> Vec { + if self.l1_contracts.is_none() + && self.l2_contracts.is_none() + && self.system_contracts.is_none() + { + return vec![ + ContractType::L1, + ContractType::L2, + ContractType::SystemContracts, + ]; + } + + let mut contracts = vec![]; + + if self.l1_contracts.unwrap_or(false) { + contracts.push(ContractType::L1); + } + if self.l2_contracts.unwrap_or(false) { + contracts.push(ContractType::L2); + } + if self.system_contracts.unwrap_or(false) { + contracts.push(ContractType::SystemContracts); + } + + contracts + } +} + +#[derive(Debug, ValueEnum, EnumIter, strum::Display, PartialEq, Eq, Clone, Copy)] +#[strum(serialize_all = "lowercase")] +pub enum ContractType { + L1, + L2, + SystemContracts, +} + +#[derive(Debug)] +struct ContractBuilder { + dir: PathBuf, + cmd: String, + msg: String, +} + +impl ContractBuilder { + fn new(ecosystem: &EcosystemConfig, contract_type: ContractType) -> Self { + match contract_type { + ContractType::L1 => Self { + dir: ecosystem.path_to_foundry(), + cmd: "forge build".to_string(), + msg: MSG_BUILDING_L1_CONTRACTS_SPINNER.to_string(), + }, + ContractType::L2 => Self { + dir: ecosystem.link_to_code.clone(), + cmd: "yarn l2-contracts build".to_string(), + msg: MSG_BUILDING_L2_CONTRACTS_SPINNER.to_string(), + }, + ContractType::SystemContracts => Self { + dir: ecosystem.link_to_code.join("contracts"), + cmd: "yarn sc build".to_string(), + msg: MSG_BUILDING_SYSTEM_CONTRACTS_SPINNER.to_string(), + }, + } + } + + fn build(&self, shell: &Shell) -> anyhow::Result<()> { + let spinner = Spinner::new(&self.msg); + let _dir_guard = shell.push_dir(&self.dir); + + let mut args = self.cmd.split_whitespace().collect::>(); + let command = args.remove(0); // It's safe to unwrap here because we know that the vec is not empty + let mut cmd = cmd!(shell, "{command}"); + + for arg in args { + cmd = cmd.arg(arg); + } + + Cmd::new(cmd).run()?; + + spinner.finish(); + Ok(()) + } +} + +pub fn run(shell: &Shell, args: ContractsArgs) -> anyhow::Result<()> { + let contracts = args.contracts(); + if contracts.is_empty() { + logger::outro(MSG_NOTHING_TO_BUILD_MSG); + return Ok(()); + } + + logger::info(MSG_BUILDING_CONTRACTS); + + let ecosystem = EcosystemConfig::from_file(shell)?; + let link_to_code = ecosystem.link_to_code.clone(); + + let spinner = Spinner::new(MSG_CONTRACTS_DEPS_SPINNER); + let _dir_guard = shell.push_dir(&link_to_code); + Cmd::new(cmd!(shell, "yarn install")).run()?; + spinner.finish(); + + contracts + .iter() + .map(|contract| ContractBuilder::new(&ecosystem, *contract)) + .try_for_each(|builder| builder.build(shell))?; + + logger::outro(MSG_BUILDING_CONTRACTS_SUCCESS); + + Ok(()) +} diff --git a/zk_toolbox/crates/zk_supervisor/src/commands/mod.rs b/zk_toolbox/crates/zk_supervisor/src/commands/mod.rs index 181ce50c2134..e45512d50d89 100644 --- a/zk_toolbox/crates/zk_supervisor/src/commands/mod.rs +++ b/zk_toolbox/crates/zk_supervisor/src/commands/mod.rs @@ -1,4 +1,5 @@ pub mod clean; +pub mod contracts; pub mod database; pub mod fmt; pub mod lint; diff --git a/zk_toolbox/crates/zk_supervisor/src/main.rs b/zk_toolbox/crates/zk_supervisor/src/main.rs index 9a1c1ad74bcd..6b5bfa46943e 100644 --- a/zk_toolbox/crates/zk_supervisor/src/main.rs +++ b/zk_toolbox/crates/zk_supervisor/src/main.rs @@ -1,6 +1,7 @@ use clap::{Parser, Subcommand}; use commands::{ - database::DatabaseCommands, lint::LintArgs, snapshot::SnapshotCommands, test::TestCommands, + contracts::ContractsArgs, database::DatabaseCommands, lint::LintArgs, + snapshot::SnapshotCommands, test::TestCommands, }; use common::{ check_general_prerequisites, @@ -10,9 +11,9 @@ use common::{ }; use config::EcosystemConfig; use messages::{ - msg_global_chain_does_not_exist, MSG_PROVER_VERSION_ABOUT, MSG_SUBCOMMAND_CLEAN, - MSG_SUBCOMMAND_DATABASE_ABOUT, MSG_SUBCOMMAND_FMT_ABOUT, MSG_SUBCOMMAND_LINT_ABOUT, - MSG_SUBCOMMAND_SNAPSHOTS_CREATOR_ABOUT, MSG_SUBCOMMAND_TESTS_ABOUT, + msg_global_chain_does_not_exist, MSG_CONTRACTS_ABOUT, MSG_PROVER_VERSION_ABOUT, + MSG_SUBCOMMAND_CLEAN, MSG_SUBCOMMAND_DATABASE_ABOUT, MSG_SUBCOMMAND_FMT_ABOUT, + MSG_SUBCOMMAND_LINT_ABOUT, MSG_SUBCOMMAND_SNAPSHOTS_CREATOR_ABOUT, MSG_SUBCOMMAND_TESTS_ABOUT, }; use xshell::Shell; @@ -49,6 +50,8 @@ enum SupervisorSubcommands { Markdown, #[command(about = MSG_PROVER_VERSION_ABOUT)] ProverVersion, + #[command(about = MSG_CONTRACTS_ABOUT)] + Contracts(ContractsArgs), } #[derive(Parser, Debug)] @@ -106,6 +109,7 @@ async fn run_subcommand(args: Supervisor, shell: &Shell) -> anyhow::Result<()> { SupervisorSubcommands::Lint(args) => commands::lint::run(shell, args)?, SupervisorSubcommands::Fmt(args) => commands::fmt::run(shell.clone(), args).await?, SupervisorSubcommands::ProverVersion => commands::prover_version::run(shell).await?, + SupervisorSubcommands::Contracts(args) => commands::contracts::run(shell, args)?, } Ok(()) } diff --git a/zk_toolbox/crates/zk_supervisor/src/messages.rs b/zk_toolbox/crates/zk_supervisor/src/messages.rs index 2374cd69f0e6..17f01e664678 100644 --- a/zk_toolbox/crates/zk_supervisor/src/messages.rs +++ b/zk_toolbox/crates/zk_supervisor/src/messages.rs @@ -13,6 +13,7 @@ pub(super) const MSG_SUBCOMMAND_DATABASE_ABOUT: &str = "Database related command pub(super) const MSG_SUBCOMMAND_TESTS_ABOUT: &str = "Run tests"; pub(super) const MSG_SUBCOMMAND_CLEAN: &str = "Clean artifacts"; pub(super) const MSG_SUBCOMMAND_LINT_ABOUT: &str = "Lint code"; +pub(super) const MSG_CONTRACTS_ABOUT: &str = "Build contracts"; pub(super) const MSG_SUBCOMMAND_FMT_ABOUT: &str = "Format code"; @@ -104,6 +105,18 @@ pub(super) const MSG_PROVER_TEST_SUCCESS: &str = "Prover tests ran successfully" pub(super) const MSG_POSTGRES_CONFIG_NOT_FOUND_ERR: &str = "Postgres config not found"; pub(super) const MSG_RESETTING_TEST_DATABASES: &str = "Resetting test databases"; +// Contract building related messages +pub(super) const MSG_NOTHING_TO_BUILD_MSG: &str = "Nothing to build!"; +pub(super) const MSG_BUILDING_CONTRACTS: &str = "Building contracts"; +pub(super) const MSG_CONTRACTS_DEPS_SPINNER: &str = "Installing dependencies.."; +pub(super) const MSG_BUILDING_L2_CONTRACTS_SPINNER: &str = "Building L2 contracts.."; +pub(super) const MSG_BUILDING_L1_CONTRACTS_SPINNER: &str = "Building L1 contracts.."; +pub(super) const MSG_BUILDING_SYSTEM_CONTRACTS_SPINNER: &str = "Building system contracts.."; +pub(super) const MSG_BUILDING_CONTRACTS_SUCCESS: &str = "Contracts built successfully"; +pub(super) const MSG_BUILD_L1_CONTRACTS_HELP: &str = "Build L1 contracts"; +pub(super) const MSG_BUILD_L2_CONTRACTS_HELP: &str = "Build L2 contracts"; +pub(super) const MSG_BUILD_SYSTEM_CONTRACTS_HELP: &str = "Build system contracts"; + // Integration tests related messages pub(super) fn msg_integration_tests_run(external_node: bool) -> String { let base = "Running integration tests";