From 835d2d38d533ccd4149fa481e8b66eb9d5815d8a Mon Sep 17 00:00:00 2001 From: Alexander Melnikov Date: Tue, 20 Aug 2024 19:51:44 +0200 Subject: [PATCH] feat: add dapp-portal support to zk_inception (#2659) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What❔ This PR introduces a new `portal` subcommand to the `zk_inception` CLI tool, enabling users to easily launch the [dapp-portal](https://github.com/matter-labs/dapp-portal) for their deployed chains. Usage: `zk_inception portal [--port 3030]` The ecosystem configurations are automatically converted to the [hyperchains](https://github.com/matter-labs/dapp-portal/tree/main/hyperchains#%EF%B8%8F-configure-manually) format, which is used to configure dapp-portal at runtime. Essentially, the following command is executed under the hood: `docker run -p PORT:3000 /path/to/portal.config.js:/usr/src/app/dist/config.js dapp-portal` ## Why ❔ Currently, running the dapp-portal requires users to manually pull the repository, install all dependencies, modify configurations, build the project, and then run it - a tedious and time-consuming process. This PR simplifies the process, allowing users to run the portal effortlessly with a single command. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- zk_toolbox/Cargo.lock | 1 + zk_toolbox/crates/common/Cargo.toml | 1 + zk_toolbox/crates/common/src/docker.rs | 15 ++ zk_toolbox/crates/common/src/ethereum.rs | 19 ++ zk_toolbox/crates/config/src/consts.rs | 3 + zk_toolbox/crates/config/src/lib.rs | 1 + zk_toolbox/crates/config/src/portal.rs | 124 +++++++++++++ zk_toolbox/crates/types/src/lib.rs | 2 + zk_toolbox/crates/types/src/token_info.rs | 18 ++ .../zk_inception/src/commands/args/mod.rs | 2 + .../zk_inception/src/commands/args/portal.rs | 12 ++ .../zk_inception/src/commands/chain/init.rs | 20 ++- .../crates/zk_inception/src/commands/mod.rs | 1 + .../zk_inception/src/commands/portal.rs | 164 ++++++++++++++++++ zk_toolbox/crates/zk_inception/src/consts.rs | 3 + zk_toolbox/crates/zk_inception/src/main.rs | 10 +- .../crates/zk_inception/src/messages.rs | 9 + 17 files changed, 397 insertions(+), 8 deletions(-) create mode 100644 zk_toolbox/crates/config/src/portal.rs create mode 100644 zk_toolbox/crates/types/src/token_info.rs create mode 100644 zk_toolbox/crates/zk_inception/src/commands/args/portal.rs create mode 100644 zk_toolbox/crates/zk_inception/src/commands/portal.rs diff --git a/zk_toolbox/Cargo.lock b/zk_toolbox/Cargo.lock index 6297687fa944..c76556272e82 100644 --- a/zk_toolbox/Cargo.lock +++ b/zk_toolbox/Cargo.lock @@ -681,6 +681,7 @@ dependencies = [ "thiserror", "tokio", "toml", + "types", "url", "xshell", ] diff --git a/zk_toolbox/crates/common/Cargo.toml b/zk_toolbox/crates/common/Cargo.toml index 18fc907d47b2..1f6fb6fd9fe1 100644 --- a/zk_toolbox/crates/common/Cargo.toml +++ b/zk_toolbox/crates/common/Cargo.toml @@ -24,6 +24,7 @@ serde_yaml.workspace = true sqlx.workspace = true tokio.workspace = true toml.workspace = true +types.workspace = true url.workspace = true xshell.workspace = true thiserror.workspace = true diff --git a/zk_toolbox/crates/common/src/docker.rs b/zk_toolbox/crates/common/src/docker.rs index f01a7955aead..0ca31383f9cc 100644 --- a/zk_toolbox/crates/common/src/docker.rs +++ b/zk_toolbox/crates/common/src/docker.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use xshell::{cmd, Shell}; use crate::cmd::Cmd; @@ -9,3 +11,16 @@ pub fn up(shell: &Shell, docker_compose_file: &str) -> anyhow::Result<()> { pub fn down(shell: &Shell, docker_compose_file: &str) -> anyhow::Result<()> { Ok(Cmd::new(cmd!(shell, "docker compose -f {docker_compose_file} down")).run()?) } + +pub fn run( + shell: &Shell, + docker_image: &str, + docker_args: HashMap, +) -> anyhow::Result<()> { + let mut args = vec![]; + for (key, value) in docker_args.iter() { + args.push(key); + args.push(value); + } + Ok(Cmd::new(cmd!(shell, "docker run {args...} {docker_image}")).run()?) +} diff --git a/zk_toolbox/crates/common/src/ethereum.rs b/zk_toolbox/crates/common/src/ethereum.rs index 93393f8a59c3..93cc524568c3 100644 --- a/zk_toolbox/crates/common/src/ethereum.rs +++ b/zk_toolbox/crates/common/src/ethereum.rs @@ -8,6 +8,7 @@ use ethers::{ providers::Middleware, types::{Address, TransactionRequest, H256}, }; +use types::TokenInfo; use crate::{logger, wallets::Wallet}; @@ -58,10 +59,28 @@ pub async fn distribute_eth( abigen!( TokenContract, r"[ + function name() external view returns (string) + function symbol() external view returns (string) + function decimals() external view returns (uint8) function mint(address to, uint256 amount) ]" ); +pub async fn get_token_info(token_address: Address, rpc_url: String) -> anyhow::Result { + let provider = Provider::::try_from(rpc_url)?; + let contract = TokenContract::new(token_address, Arc::new(provider)); + + let name = contract.name().call().await?; + let symbol = contract.symbol().call().await?; + let decimals = contract.decimals().call().await?; + + Ok(TokenInfo { + name, + symbol, + decimals, + }) +} + pub async fn mint_token( main_wallet: Wallet, token_address: Address, diff --git a/zk_toolbox/crates/config/src/consts.rs b/zk_toolbox/crates/config/src/consts.rs index c535ff52cf17..4de534b816d5 100644 --- a/zk_toolbox/crates/config/src/consts.rs +++ b/zk_toolbox/crates/config/src/consts.rs @@ -33,6 +33,9 @@ pub const ERA_OBSERBAVILITY_GIT_REPO: &str = "https://github.com/matter-labs/era pub(crate) const LOCAL_CONFIGS_PATH: &str = "configs/"; pub(crate) const LOCAL_DB_PATH: &str = "db/"; +/// Name of portal config file +pub const PORTAL_CONFIG_FILE: &str = "portal.config.js"; + /// Path to ecosystem contacts pub(crate) const ECOSYSTEM_PATH: &str = "etc/env/ecosystems"; diff --git a/zk_toolbox/crates/config/src/lib.rs b/zk_toolbox/crates/config/src/lib.rs index e2d366aeb869..4e00962229bc 100644 --- a/zk_toolbox/crates/config/src/lib.rs +++ b/zk_toolbox/crates/config/src/lib.rs @@ -25,4 +25,5 @@ mod wallets; pub mod external_node; pub mod forge_interface; +pub mod portal; pub mod traits; diff --git a/zk_toolbox/crates/config/src/portal.rs b/zk_toolbox/crates/config/src/portal.rs new file mode 100644 index 000000000000..4b68d5744cd9 --- /dev/null +++ b/zk_toolbox/crates/config/src/portal.rs @@ -0,0 +1,124 @@ +use std::path::{Path, PathBuf}; + +use serde::{Deserialize, Serialize}; +use types::TokenInfo; +use xshell::Shell; + +use crate::{ + consts::{LOCAL_CONFIGS_PATH, PORTAL_CONFIG_FILE}, + traits::{FileConfigWithDefaultName, ReadConfig, SaveConfig}, +}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct PortalRuntimeConfig { + pub node_type: String, + pub hyperchains_config: HyperchainsConfig, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct HyperchainsConfig(pub Vec); + +impl HyperchainsConfig { + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct HyperchainConfig { + pub network: NetworkConfig, + pub tokens: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct NetworkConfig { + pub id: u64, // L2 Network ID + pub key: String, // L2 Network key + pub name: String, // L2 Network name + pub rpc_url: String, // L2 RPC URL + #[serde(skip_serializing_if = "Option::is_none")] + pub block_explorer_url: Option, // L2 Block Explorer URL + #[serde(skip_serializing_if = "Option::is_none")] + pub block_explorer_api: Option, // L2 Block Explorer API + #[serde(skip_serializing_if = "Option::is_none")] + pub public_l1_network_id: Option, // Ethereum Mainnet or Ethereum Sepolia Testnet ID + #[serde(skip_serializing_if = "Option::is_none")] + pub l1_network: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct L1NetworkConfig { + pub id: u64, + pub name: String, + pub network: String, + pub native_currency: TokenInfo, + pub rpc_urls: RpcUrls, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RpcUrls { + pub default: RpcUrlConfig, + pub public: RpcUrlConfig, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RpcUrlConfig { + pub http: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct TokenConfig { + pub address: String, + pub symbol: String, + pub decimals: u8, + #[serde(skip_serializing_if = "Option::is_none")] + pub l1_address: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +impl PortalRuntimeConfig { + pub fn get_config_path(ecosystem_base_path: &Path) -> PathBuf { + ecosystem_base_path + .join(LOCAL_CONFIGS_PATH) + .join(PORTAL_CONFIG_FILE) + } +} + +impl FileConfigWithDefaultName for PortalRuntimeConfig { + const FILE_NAME: &'static str = PORTAL_CONFIG_FILE; +} + +impl SaveConfig for PortalRuntimeConfig { + fn save(&self, shell: &Shell, path: impl AsRef) -> anyhow::Result<()> { + // The dapp-portal is served as a pre-built static app in a Docker image. + // It uses a JavaScript file (config.js) that injects the configuration at runtime + // by overwriting the '##runtimeConfig' property of the window object. + // Therefore, we generate a JavaScript file instead of a JSON file. + // This file will be mounted to the Docker image when it runs. + let json = serde_json::to_string_pretty(&self)?; + let config_js_content = format!("window['##runtimeConfig'] = {};", json); + Ok(shell.write_file(path, config_js_content.as_bytes())?) + } +} + +impl ReadConfig for PortalRuntimeConfig { + fn read(shell: &Shell, path: impl AsRef) -> anyhow::Result { + let config_js_content = shell.read_file(path)?; + // Extract the JSON part from the JavaScript file + let json_start = config_js_content + .find('{') + .ok_or_else(|| anyhow::anyhow!("Invalid config file format"))?; + let json_end = config_js_content + .rfind('}') + .ok_or_else(|| anyhow::anyhow!("Invalid config file format"))?; + let json_str = &config_js_content[json_start..=json_end]; + // Parse the JSON into PortalRuntimeConfig + let config: PortalRuntimeConfig = serde_json::from_str(json_str)?; + Ok(config) + } +} diff --git a/zk_toolbox/crates/types/src/lib.rs b/zk_toolbox/crates/types/src/lib.rs index 4cc7f160a45b..8b6470571051 100644 --- a/zk_toolbox/crates/types/src/lib.rs +++ b/zk_toolbox/crates/types/src/lib.rs @@ -1,11 +1,13 @@ mod base_token; mod l1_network; mod prover_mode; +mod token_info; mod wallet_creation; pub use base_token::*; pub use l1_network::*; pub use prover_mode::*; +pub use token_info::*; pub use wallet_creation::*; pub use zksync_basic_types::{ commitment::L1BatchCommitmentMode, protocol_version::ProtocolSemanticVersion, diff --git a/zk_toolbox/crates/types/src/token_info.rs b/zk_toolbox/crates/types/src/token_info.rs new file mode 100644 index 000000000000..8271f8e0c849 --- /dev/null +++ b/zk_toolbox/crates/types/src/token_info.rs @@ -0,0 +1,18 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct TokenInfo { + pub name: String, + pub symbol: String, + pub decimals: u8, +} + +impl TokenInfo { + pub fn eth() -> Self { + Self { + name: "Ether".to_string(), + symbol: "ETH".to_string(), + decimals: 18, + } + } +} diff --git a/zk_toolbox/crates/zk_inception/src/commands/args/mod.rs b/zk_toolbox/crates/zk_inception/src/commands/args/mod.rs index d18b05c910e5..a27b653edf52 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/args/mod.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/args/mod.rs @@ -1,7 +1,9 @@ pub use containers::*; +pub use portal::*; pub use run_server::*; pub use update::*; mod containers; +mod portal; mod run_server; mod update; diff --git a/zk_toolbox/crates/zk_inception/src/commands/args/portal.rs b/zk_toolbox/crates/zk_inception/src/commands/args/portal.rs new file mode 100644 index 000000000000..e31058aad5d0 --- /dev/null +++ b/zk_toolbox/crates/zk_inception/src/commands/args/portal.rs @@ -0,0 +1,12 @@ +use clap::Parser; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Parser)] +pub struct PortalArgs { + #[clap( + long, + default_value = "3030", + help = "The port number for the portal app" + )] + pub port: u16, +} diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/init.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/init.rs index 05599ef94e48..9d1c0d543ee0 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/init.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/init.rs @@ -20,17 +20,21 @@ use xshell::Shell; use crate::{ accept_ownership::accept_admin, - commands::chain::{ - args::init::{InitArgs, InitArgsFinal}, - deploy_l2_contracts, deploy_paymaster, - genesis::genesis, - set_token_multiplier_setter::set_token_multiplier_setter, + commands::{ + chain::{ + args::init::{InitArgs, InitArgsFinal}, + deploy_l2_contracts, deploy_paymaster, + genesis::genesis, + set_token_multiplier_setter::set_token_multiplier_setter, + }, + portal::create_and_save_portal_config, }, consts::AMOUNT_FOR_DISTRIBUTION_TO_WALLETS, messages::{ msg_initializing_chain, MSG_ACCEPTING_ADMIN_SPINNER, MSG_CHAIN_INITIALIZED, MSG_CHAIN_NOT_FOUND_ERR, MSG_DISTRIBUTING_ETH_SPINNER, MSG_GENESIS_DATABASE_ERR, - MSG_MINT_BASE_TOKEN_SPINNER, MSG_REGISTERING_CHAIN_SPINNER, MSG_SELECTED_CONFIG, + MSG_MINT_BASE_TOKEN_SPINNER, MSG_PORTAL_FAILED_TO_CREATE_CONFIG_ERR, + MSG_REGISTERING_CHAIN_SPINNER, MSG_SELECTED_CONFIG, MSG_UPDATING_TOKEN_MULTIPLIER_SETTER_SPINNER, }, utils::forge::{check_the_balance, fill_forge_private_key}, @@ -145,6 +149,10 @@ pub async fn init( .await .context(MSG_GENESIS_DATABASE_ERR)?; + create_and_save_portal_config(ecosystem_config, shell) + .await + .context(MSG_PORTAL_FAILED_TO_CREATE_CONFIG_ERR)?; + Ok(()) } diff --git a/zk_toolbox/crates/zk_inception/src/commands/mod.rs b/zk_toolbox/crates/zk_inception/src/commands/mod.rs index 5eea6e8a5a1a..0ac363beb2da 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/mod.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/mod.rs @@ -4,6 +4,7 @@ pub mod containers; pub mod contract_verifier; pub mod ecosystem; pub mod external_node; +pub mod portal; pub mod prover; pub mod server; pub mod update; diff --git a/zk_toolbox/crates/zk_inception/src/commands/portal.rs b/zk_toolbox/crates/zk_inception/src/commands/portal.rs new file mode 100644 index 000000000000..cc939f3fb3ea --- /dev/null +++ b/zk_toolbox/crates/zk_inception/src/commands/portal.rs @@ -0,0 +1,164 @@ +use std::{collections::HashMap, path::Path}; + +use anyhow::{anyhow, Context}; +use common::{docker, ethereum, logger}; +use config::{ + portal::*, + traits::{ReadConfig, SaveConfig}, + ChainConfig, EcosystemConfig, +}; +use ethers::types::Address; +use types::{BaseToken, TokenInfo}; +use xshell::Shell; + +use crate::{ + commands::args::PortalArgs, + consts::{L2_BASE_TOKEN_ADDRESS, PORTAL_DOCKER_CONTAINER_PORT, PORTAL_DOCKER_IMAGE}, + messages::{ + msg_portal_starting_on, MSG_PORTAL_CONFIG_IS_EMPTY_ERR, + MSG_PORTAL_FAILED_TO_CREATE_CONFIG_ERR, MSG_PORTAL_FAILED_TO_RUN_DOCKER_ERR, + }, +}; + +async fn create_hyperchain_config(chain_config: &ChainConfig) -> anyhow::Result { + // Get L2 RPC URL from general config + let general_config = chain_config.get_general_config()?; + let rpc_url = general_config + .api_config + .as_ref() + .map(|api_config| &api_config.web3_json_rpc.http_url) + .context("api_config")?; + // Get L1 RPC URL from secrects config + let secrets_config = chain_config.get_secrets_config()?; + let l1_rpc_url = secrets_config + .l1 + .as_ref() + .map(|l1| l1.l1_rpc_url.expose_str()) + .context("l1")?; + // Build L1 network config + let l1_network = Some(L1NetworkConfig { + id: chain_config.l1_network.chain_id(), + name: chain_config.l1_network.to_string(), + network: chain_config.l1_network.to_string().to_lowercase(), + native_currency: TokenInfo::eth(), + rpc_urls: RpcUrls { + default: RpcUrlConfig { + http: vec![l1_rpc_url.to_string()], + }, + public: RpcUrlConfig { + http: vec![l1_rpc_url.to_string()], + }, + }, + }); + // Base token: + let (base_token_addr, base_token_info) = if chain_config.base_token == BaseToken::eth() { + (format!("{:?}", Address::zero()), TokenInfo::eth()) + } else { + ( + format!("{:?}", chain_config.base_token.address), + ethereum::get_token_info(chain_config.base_token.address, l1_rpc_url.to_string()) + .await?, + ) + }; + let tokens = vec![TokenConfig { + address: L2_BASE_TOKEN_ADDRESS.to_string(), + l1_address: Some(base_token_addr.to_string()), + symbol: base_token_info.symbol, + decimals: base_token_info.decimals, + name: Some(base_token_info.name.to_string()), + }]; + // Build hyperchain config + Ok(HyperchainConfig { + network: NetworkConfig { + id: chain_config.chain_id.as_u64(), + key: chain_config.name.clone(), + name: chain_config.name.clone(), + rpc_url: rpc_url.to_string(), + l1_network, + public_l1_network_id: None, + block_explorer_url: None, + block_explorer_api: None, + }, + tokens, + }) +} + +async fn create_hyperchains_config( + chain_configs: &[ChainConfig], +) -> anyhow::Result { + let mut hyperchain_configs = Vec::new(); + for chain_config in chain_configs { + if let Ok(config) = create_hyperchain_config(chain_config).await { + hyperchain_configs.push(config) + } + } + Ok(HyperchainsConfig(hyperchain_configs)) +} + +pub async fn create_portal_config( + ecosystem_config: &EcosystemConfig, +) -> anyhow::Result { + let chains: Vec = ecosystem_config.list_of_chains(); + let mut chain_configs = Vec::new(); + for chain in chains { + if let Some(chain_config) = ecosystem_config.load_chain(Some(chain.clone())) { + chain_configs.push(chain_config) + } + } + let hyperchains_config = create_hyperchains_config(&chain_configs).await?; + if hyperchains_config.is_empty() { + anyhow::bail!("Failed to create any valid hyperchain config") + } + let runtime_config = PortalRuntimeConfig { + node_type: "hyperchain".to_string(), + hyperchains_config, + }; + Ok(runtime_config) +} + +pub async fn create_and_save_portal_config( + ecosystem_config: &EcosystemConfig, + shell: &Shell, +) -> anyhow::Result { + let portal_config = create_portal_config(ecosystem_config).await?; + let config_path = PortalRuntimeConfig::get_config_path(&shell.current_dir()); + portal_config.save(shell, config_path)?; + Ok(portal_config) +} + +pub async fn run(shell: &Shell, args: PortalArgs) -> anyhow::Result<()> { + let ecosystem_config: EcosystemConfig = EcosystemConfig::from_file(shell)?; + let config_path = PortalRuntimeConfig::get_config_path(&shell.current_dir()); + logger::info(format!( + "Using portal config file at {}", + config_path.display() + )); + + let portal_config = match PortalRuntimeConfig::read(shell, &config_path) { + Ok(config) => config, + Err(_) => create_and_save_portal_config(&ecosystem_config, shell) + .await + .context(MSG_PORTAL_FAILED_TO_CREATE_CONFIG_ERR)?, + }; + if portal_config.hyperchains_config.is_empty() { + return Err(anyhow!(MSG_PORTAL_CONFIG_IS_EMPTY_ERR)); + } + + logger::info(msg_portal_starting_on("127.0.0.1", args.port)); + run_portal(shell, &config_path, args.port)?; + Ok(()) +} + +fn run_portal(shell: &Shell, config_file_path: &Path, port: u16) -> anyhow::Result<()> { + let port_mapping = format!("{}:{}", port, PORTAL_DOCKER_CONTAINER_PORT); + let volume_mapping = format!("{}:/usr/src/app/dist/config.js", config_file_path.display()); + + let mut docker_args: HashMap = HashMap::new(); + docker_args.insert("--platform".to_string(), "linux/amd64".to_string()); + docker_args.insert("-p".to_string(), port_mapping); + docker_args.insert("-v".to_string(), volume_mapping); + + docker::run(shell, PORTAL_DOCKER_IMAGE, docker_args) + .with_context(|| MSG_PORTAL_FAILED_TO_RUN_DOCKER_ERR)?; + Ok(()) +} diff --git a/zk_toolbox/crates/zk_inception/src/consts.rs b/zk_toolbox/crates/zk_inception/src/consts.rs index d9b61d49185a..7463dc28570e 100644 --- a/zk_toolbox/crates/zk_inception/src/consts.rs +++ b/zk_toolbox/crates/zk_inception/src/consts.rs @@ -7,3 +7,6 @@ pub const PROVER_STORE_MAX_RETRIES: u16 = 10; pub const DEFAULT_CREDENTIALS_FILE: &str = "~/.config/gcloud/application_default_credentials.json"; pub const DEFAULT_PROOF_STORE_DIR: &str = "artifacts"; pub const BELLMAN_CUDA_DIR: &str = "era-bellman-cuda"; +pub const L2_BASE_TOKEN_ADDRESS: &str = "0x000000000000000000000000000000000000800A"; +pub const PORTAL_DOCKER_IMAGE: &str = "matterlabs/dapp-portal"; +pub const PORTAL_DOCKER_CONTAINER_PORT: u16 = 3000; diff --git a/zk_toolbox/crates/zk_inception/src/main.rs b/zk_toolbox/crates/zk_inception/src/main.rs index 2b5bdeb9c1a5..8895b212a59f 100644 --- a/zk_toolbox/crates/zk_inception/src/main.rs +++ b/zk_toolbox/crates/zk_inception/src/main.rs @@ -13,8 +13,11 @@ use config::EcosystemConfig; use xshell::Shell; use crate::commands::{ - args::RunServerArgs, chain::ChainCommands, ecosystem::EcosystemCommands, - external_node::ExternalNodeCommands, prover::ProverCommands, + args::{PortalArgs, RunServerArgs}, + chain::ChainCommands, + ecosystem::EcosystemCommands, + external_node::ExternalNodeCommands, + prover::ProverCommands, }; pub mod accept_ownership; @@ -56,6 +59,8 @@ pub enum InceptionSubcommands { /// Run contract verifier #[command(subcommand)] ContractVerifier(ContractVerifierCommands), + /// Run dapp-portal + Portal(PortalArgs), /// Update zkSync #[command(alias = "u")] Update(UpdateArgs), @@ -118,6 +123,7 @@ async fn run_subcommand(inception_args: Inception, shell: &Shell) -> anyhow::Res InceptionSubcommands::ContractVerifier(args) => { commands::contract_verifier::run(shell, args).await? } + InceptionSubcommands::Portal(args) => commands::portal::run(shell, args).await?, InceptionSubcommands::Update(args) => commands::update::run(shell, args)?, InceptionSubcommands::Markdown => { clap_markdown::print_help_markdown::(); diff --git a/zk_toolbox/crates/zk_inception/src/messages.rs b/zk_toolbox/crates/zk_inception/src/messages.rs index 441a1e5c8538..f0e46aaf4869 100644 --- a/zk_toolbox/crates/zk_inception/src/messages.rs +++ b/zk_toolbox/crates/zk_inception/src/messages.rs @@ -221,6 +221,15 @@ pub(super) const MSG_STARTING_SERVER: &str = "Starting server"; pub(super) const MSG_FAILED_TO_RUN_SERVER_ERR: &str = "Failed to start server"; pub(super) const MSG_PREPARING_EN_CONFIGS: &str = "Preparing External Node config"; +/// Portal related messages +pub(super) const MSG_PORTAL_CONFIG_IS_EMPTY_ERR: &str = "Hyperchains config is empty"; +pub(super) const MSG_PORTAL_FAILED_TO_CREATE_CONFIG_ERR: &str = "Failed to create portal config"; +pub(super) const MSG_PORTAL_FAILED_TO_RUN_DOCKER_ERR: &str = + "Failed to run portal docker container"; +pub(super) fn msg_portal_starting_on(host: &str, port: u16) -> String { + format!("Starting portal on http://{host}:{port}") +} + /// Forge utils related messages pub(super) const MSG_DEPLOYER_PK_NOT_SET_ERR: &str = "Deployer private key is not set";