diff --git a/.github/scripts/gateway_zktoken.sh b/.github/scripts/gateway_zktoken.sh new file mode 100755 index 00000000000..7b4b8cd72d1 --- /dev/null +++ b/.github/scripts/gateway_zktoken.sh @@ -0,0 +1,74 @@ +sudo rm -rf ./volumes && zk_supervisor clean containers && zk_inception up -o false + +zk_inception ecosystem init --deploy-paymaster --deploy-erc20 \ + --deploy-ecosystem --l1-rpc-url=http://localhost:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_localhost_era \ + --prover-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --prover-db-name=zksync_prover_localhost_era \ + --ignore-prerequisites --observability=false --skip-submodules-checkout \ + --chain era \ + --verbose + +zk_inception server --ignore-prerequisites --chain era &> ./era.log & + +sleep 20 + +zk_inception chain deploy-and-bridge-zk --chain era --verbose + +# Define the path to the TOML file +TOML_FILE="contracts/l1-contracts/script-out/output-deploy-zk-token.toml" + +# Extract the l1Address from the TOML file +zkL1Address=$(grep -A 1 "\[ZK.l1Address\]" "$TOML_FILE" | grep "l1Address" | awk -F' = ' '{print $2}' | tr -d '"' | tr -d ' ' | tr -d '\n') + +# Check if the address starts with 0x and remove it +if [[ $zkL1Address == 0x* ]]; then + zkL1Address="${zkL1Address:2}" +fi + +# Output the extracted and sliced l1Address (for debugging purposes) +echo "Sliced l1Address: $zkL1Address" + +zk_inception chain create \ + --chain-name gateway \ + --chain-id 505 \ + --prover-mode no-proofs \ + --wallet-creation localhost \ + --l1-batch-commit-data-generator-mode rollup \ + --base-token-address $zkL1Address \ + --base-token-price-nominator 1 \ + --base-token-price-denominator 1 \ + --set-as-default false \ + --ignore-prerequisites --skip-submodules-checkout --skip-contract-compilation-override + +zk_inception chain init \ + --deploy-paymaster \ + --l1-rpc-url=http://localhost:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_localhost_gateway \ + --prover-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --prover-db-name=zksync_prover_localhost_gateway \ + --chain gateway --skip-submodules-checkout + +zk_inception chain convert-to-gateway --chain gateway --ignore-prerequisites + +SERVER_PID=$(lsof -t -i :3075) + +kill -2 $SERVER_PID + +zk_inception server --ignore-prerequisites --chain gateway &> ./gateway.log & + +sleep 20 + +zk_inception chain migrate-to-gateway --chain era --gateway-chain-name gateway + +zk_inception chain migrate-from-gateway --chain era --gateway-chain-name gateway + +zk_inception chain migrate-to-gateway --chain era --gateway-chain-name gateway + +zk_inception server --ignore-prerequisites --chain era &> ./rollup.log & + +sleep 20 + +zk_supervisor test integration --no-deps --ignore-prerequisites --chain era \ No newline at end of file diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index aa7edefda8a..3ff3a2966f8 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -34,7 +34,7 @@ jobs: echo "SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com" >> .env echo "SCCACHE_GCS_RW_MODE=READ_WRITE" >> .env echo "RUSTC_WRAPPER=sccache" >> .env - + # TODO: Remove when we after upgrade of hardhat-plugins - name: pre-download compilers run: | @@ -176,6 +176,10 @@ jobs: submodules: "recursive" fetch-depth: 0 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y lsof - name: Setup environment run: | @@ -234,12 +238,31 @@ jobs: ci_run git config --global --add safe.directory /usr/src/zksync/contracts ci_run zkstack ecosystem init --deploy-paymaster --deploy-erc20 \ - --deploy-ecosystem --l1-rpc-url=http://localhost:8545 \ - --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ - --server-db-name=zksync_server_localhost_era \ - --ignore-prerequisites --verbose \ - --observability=false + --deploy-ecosystem --l1-rpc-url=http://localhost:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_localhost_era \ + --ignore-prerequisites --verbose \ + --observability=false + + - name: Start Era server + run: | + ci_run zkstack server --ignore-prerequisites --chain era &> ${{ env.SERVER_LOGS_DIR }}/rollup.log & + ci_run sleep 20 # Wait for server to start + + - name: Deploy and bridge ZK token on Era chain + run: ci_run zk_inception chain deploy-and-bridge-zk --chain era --verbose + + - name: Extract ZK token L1 address + run: | + TOML_FILE="contracts/l1-contracts/script-out/output-deploy-zk-token.toml" + zkL1Address=$(grep -A 1 "\[ZK.l1Address\]" "$TOML_FILE" | grep "l1Address" | awk -F' = ' '{print $2}' | tr -d '"' | tr -d ' ' | tr -d '\n') + if [[ $zkL1Address == 0x* ]]; then + zkL1Address="${zkL1Address:2}" + fi + echo "ZK_L1_ADDRESS=$zkL1Address" >> $GITHUB_ENV + echo "Extracted ZK L1 Address: $zkL1Address" + - name: Read Custom Token address and set as environment variable run: | CUSTOM_TOKEN_ADDRESS=$(awk -F": " '/tokens:/ {found_tokens=1} found_tokens && /DAI:/ {found_dai=1} found_dai && /address:/ {print $2; exit}' ./configs/erc20.yaml) @@ -267,6 +290,8 @@ jobs: --server-db-name=zksync_server_localhost_validium \ --chain validium + ci_run zk_inception chain deploy-and-bridge-zk --chain era --only-funding-tx --verbose + - name: Create and initialize chain with Custom Token run: | ci_run zkstack chain create \ @@ -288,6 +313,8 @@ jobs: --server-db-name=zksync_server_localhost_custom_token \ --chain custom_token + ci_run zkstack chain deploy-and-bridge-zk --chain era --only-funding-tx --verbose + # - name: Create and register chain with transactions signed "offline" # run: | # ci_run zkstack chain create \ @@ -323,27 +350,54 @@ jobs: # exit 1 # fi + - name: Shut down Era server (sends the kill command to all era zksync_server proccesses) + run: | + # Find the PID using ports 3071 or 3075 + PIDS=$(sudo lsof -ti :3071 -ti :3075 || true) + + # If any PIDs are found, kill them + if [ -n "$PIDS" ]; then + echo "Killing process(es) on ports 3071 or 3075 with PID(s): $PIDS" + for PID in $PIDS; do + # Attempt graceful shutdown + sudo kill -2 "$PID" || true + sleep 10 + + # Check if still running, then force kill + if sudo kill -0 "$PID" 2>/dev/null; then + echo "Process $PID still running, forcing termination." + sudo kill -9 "$PID" || true + fi + # sudo kill -9 "$PID" || true # Use SIGKILL to ensure termination + done + else + echo "No processes found on ports 3071 or 3075." + fi + + # Ensure the script always exits successfully + exit 0 + - name: Create and initialize Consensus chain run: | ci_run zkstack chain create \ - --chain-name consensus \ - --chain-id sequential \ - --prover-mode no-proofs \ - --wallet-creation localhost \ - --l1-batch-commit-data-generator-mode validium \ - --base-token-address ${{ env.CUSTOM_TOKEN_ADDRESS }} \ - --base-token-price-nominator 3 \ - --base-token-price-denominator 2 \ - --set-as-default false \ - --ignore-prerequisites + --chain-name consensus \ + --chain-id sequential \ + --prover-mode no-proofs \ + --wallet-creation localhost \ + --l1-batch-commit-data-generator-mode validium \ + --base-token-address ${{ env.CUSTOM_TOKEN_ADDRESS }} \ + --base-token-price-nominator 3 \ + --base-token-price-denominator 2 \ + --set-as-default false \ + --ignore-prerequisites ci_run zkstack chain init \ - --deploy-paymaster \ - --l1-rpc-url=http://localhost:8545 \ - --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ - --server-db-name=zksync_server_localhost_consensus \ - --chain consensus - + --deploy-paymaster \ + --l1-rpc-url=http://localhost:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_localhost_consensus \ + --chain consensus + - name: Export chain list to environment variable run: | CHAINS="era,validium,custom_token,consensus" @@ -352,24 +406,24 @@ jobs: - name: Initialize gateway chain run: | ci_run zkstack chain create \ - --chain-name gateway \ - --chain-id 505 \ - --prover-mode no-proofs \ - --wallet-creation localhost \ - --l1-batch-commit-data-generator-mode rollup \ - --base-token-address 0x0000000000000000000000000000000000000001 \ - --base-token-price-nominator 1 \ - --base-token-price-denominator 1 \ - --set-as-default false \ - --ignore-prerequisites - + --chain-name gateway \ + --chain-id 505 \ + --prover-mode no-proofs \ + --wallet-creation localhost \ + --l1-batch-commit-data-generator-mode rollup \ + --base-token-address ${{ env.ZK_L1_ADDRESS }} \ + --base-token-price-nominator 1 \ + --base-token-price-denominator 1 \ + --set-as-default false \ + --ignore-prerequisites + ci_run zkstack chain init \ - --deploy-paymaster \ - --l1-rpc-url=http://localhost:8545 \ - --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ - --server-db-name=zksync_server_localhost_gateway \ - --chain gateway - + --deploy-paymaster \ + --l1-rpc-url=http://localhost:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_localhost_gateway \ + --chain gateway + ci_run zkstack chain convert-to-gateway --chain gateway --ignore-prerequisites - name: Run gateway diff --git a/contracts b/contracts index 53b0283f82f..1dbbf05293d 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 53b0283f82f4262c973eb3faed56ee8f6cda47b9 +Subproject commit 1dbbf05293dc604dd9fa8f40d8ad6b7ab2838d7b diff --git a/etc/env/file_based/genesis.yaml b/etc/env/file_based/genesis.yaml index 212c17c2bf4..cf5de578085 100644 --- a/etc/env/file_based/genesis.yaml +++ b/etc/env/file_based/genesis.yaml @@ -1,5 +1,5 @@ -genesis_root: 0x526a5d3e384ff95a976283c79a976e0a2fb749e4631233f29d3765201efd937d -genesis_batch_commitment: 0xb9794246425fd654cf6a4c2e9adfdd48aaaf97bf3b8ba6bdc88e1d141bcfa5b3 +genesis_root: 0x59907f1d1a7d24b6d93f4654e413309e6cb58c8f01c141ee5941dba2270e8303 +genesis_batch_commitment: 0x7484436f4406a9b6d2966dab11c0f15b0b27d087f447519492829a5587be3ed7 genesis_rollup_leaf_index: 64 default_aa_hash: 0x0100055d3993e14104994ca4d8cfa91beb9b544ee86894b45708b4824d832ff2 bootloader_hash: 0x010008c753336bc8d1ddca235602b9f31d346412b2d463cd342899f7bfb73baf diff --git a/zkstack_cli/crates/common/src/ethereum.rs b/zkstack_cli/crates/common/src/ethereum.rs index 2100746fecf..0cf6f41e6d6 100644 --- a/zkstack_cli/crates/common/src/ethereum.rs +++ b/zkstack_cli/crates/common/src/ethereum.rs @@ -62,6 +62,7 @@ abigen!( function symbol() external view returns (string) function decimals() external view returns (uint8) function mint(address to, uint256 amount) + function transfer(address to, uint256 amount) ]" ); @@ -96,17 +97,21 @@ pub async fn mint_token( let contract = TokenContract::new(token_address, client); let mut pending_calls = vec![]; - for address in addresses { + let mut pending_txs = vec![]; + for address in addresses.clone() { pending_calls.push(contract.mint(address, amount.into())); } - let mut pending_txs = vec![]; + for address in addresses { + pending_calls.push(contract.transfer(address, amount.into())); + } + for call in &pending_calls { let call = call.send().await; match call { // It's safe to set such low number of confirmations and low interval for localhost Ok(call) => pending_txs.push(call.confirmations(3).interval(Duration::from_millis(30))), - Err(e) => logger::error(format!("Minting is not successful {e}")), + Err(e) => logger::error(format!("Minting or transfer is not successful {e}")), } } diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs index 17b2bac38a3..00a53731481 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs @@ -92,6 +92,13 @@ impl Default for Erc20DeploymentConfig { implementation: String::from("TestnetERC20Token.sol"), mint: U256::from_str("9000000000000000000000").unwrap(), }, + Erc20DeploymentTokensConfig { + name: String::from("ZK"), + symbol: String::from("ZK"), + decimals: 8, + implementation: String::from("TestnetERC20Token.sol"), + mint: U256::from_str("9000000000000000000000").unwrap(), + }, ], } } diff --git a/zkstack_cli/crates/config/src/forge_interface/script_params.rs b/zkstack_cli/crates/config/src/forge_interface/script_params.rs index f05b1abfb37..1fb1e976341 100644 --- a/zkstack_cli/crates/config/src/forge_interface/script_params.rs +++ b/zkstack_cli/crates/config/src/forge_interface/script_params.rs @@ -79,3 +79,9 @@ pub const GATEWAY_PREPARATION: ForgeScriptParams = ForgeScriptParams { output: "script-out/output-gateway-preparation-l1.toml", script_path: "deploy-scripts/GatewayPreparation.s.sol", }; + +pub const ZK_PREPARATION: ForgeScriptParams = ForgeScriptParams { + input: "script-config/config-deploy-zk.toml", + output: "script-out/output-deploy-zk-token.toml", + script_path: "deploy-scripts/DeployZKAndBridgeToL1.s.sol", +}; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/common.rs b/zkstack_cli/crates/zkstack/src/commands/chain/common.rs index e0aa0b4e047..b5df52d6f5f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/common.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/common.rs @@ -53,7 +53,13 @@ pub async fn mint_base_token( let wallets = ecosystem_config.get_wallets()?; let chain_wallets = chain_config.get_wallets_config()?; let base_token = &chain_config.base_token; - let addresses = vec![wallets.governor.address, chain_wallets.governor.address]; + let addresses = vec![ + wallets.governor.address, + wallets.deployer.unwrap().address, + chain_wallets.governor.address, + chain_wallets.deployer.unwrap().address, + ]; + let amount = AMOUNT_FOR_DISTRIBUTION_TO_WALLETS * base_token.nominator as u128 / base_token.denominator as u128; common::ethereum::mint_token( diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_and_bridge_zk.rs b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_and_bridge_zk.rs new file mode 100644 index 00000000000..f5e3f015330 --- /dev/null +++ b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_and_bridge_zk.rs @@ -0,0 +1,321 @@ +use std::{fs, fs::File, io::BufReader, path::PathBuf}; + +use anyhow::Context; +use clap::Parser; +use common::{ + config::global_config, + forge::{Forge, ForgeScriptArgs}, + withdraw::ZKSProvider, +}; +use config::{forge_interface::script_params::ZK_PREPARATION, EcosystemConfig}; +use ethers::{ + abi::parse_abi, + contract::BaseContract, + providers::{Http, Middleware, Provider}, + types::Bytes, + utils::hex, +}; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use xshell::Shell; +use zksync_basic_types::{H256, U256, U64}; +use zksync_types::L2ChainId; +use zksync_web3_decl::client::{Client, L2}; + +use crate::{ + messages::{MSG_CHAIN_NOT_INITIALIZED, MSG_L1_SECRETS_MUST_BE_PRESENTED}, + utils::forge::{check_the_balance, fill_forge_private_key}, +}; + +#[derive(Debug, Serialize, Deserialize, Parser)] +pub struct DeployAndBridgeZKArgs { + /// All ethereum environment related arguments + #[clap(flatten)] + #[serde(flatten)] + pub forge_args: ForgeScriptArgs, + + #[clap(long)] + pub only_funding_tx: bool, +} + +#[derive(Debug, Deserialize)] +struct JsonData { + transactions: Vec, +} + +#[derive(Debug, Deserialize)] +struct Transaction { + hash: String, +} + +lazy_static! { + static ref DEPLOY_AND_BRIDGE_ZK_TOKEN_INTERFACE: BaseContract = BaseContract::from( + parse_abi(&[ + "function run() public", + "function supplyEraWallet(address addr, uint256 amount) public", + "function finalizeZkTokenWithdrawal(uint256 chainId, uint256 l2BatchNumber, uint256 l2MessageIndex, uint16 l2TxNumberInBatch, bytes memory message, bytes32[] memory merkleProof) public", + "function saveL1Address() public", + "function fundChainGovernor() public" + ]) + .unwrap(), + ); +} + +fn find_latest_json_file(directory: &str) -> Option { + fs::read_dir(directory) + .ok()? + .flatten() + .map(|entry| entry.path()) + .find(|path| { + path.file_name() + .and_then(|name| name.to_str()) + .map_or(false, |name| name.ends_with("-latest.json")) + }) +} + +pub async fn run(args: DeployAndBridgeZKArgs, shell: &Shell) -> anyhow::Result<()> { + // Setup + let ecosystem_config = EcosystemConfig::from_file(shell)?; + + let chain_name = global_config().chain_name.clone(); + let chain_config = ecosystem_config + .load_chain(chain_name) + .context(MSG_CHAIN_NOT_INITIALIZED)?; + + let l1_url = chain_config + .get_secrets_config()? + .l1 + .context(MSG_L1_SECRETS_MUST_BE_PRESENTED)? + .l1_rpc_url + .expose_str() + .to_string(); + + let era_chain_id = U256::from(chain_config.chain_id.0); + let era_provider = Provider::::try_from( + chain_config + .get_general_config() + .unwrap() + .api_config + .unwrap() + .web3_json_rpc + .http_url, + )?; + let era_client: Client = Client::http( + chain_config + .get_general_config() + .unwrap() + .api_config + .unwrap() + .web3_json_rpc + .http_url + .parse() + .unwrap(), + )? + .for_network(L2::from(L2ChainId(chain_config.chain_id.0))) + .build(); + + if args.only_funding_tx { + call_script( + shell, + args.forge_args.clone(), + &DEPLOY_AND_BRIDGE_ZK_TOKEN_INTERFACE + .encode("fundChainGovernor", ()) + .unwrap(), + &ecosystem_config, + chain_config.get_wallets_config()?.governor_private_key(), + l1_url.clone(), + ) + .await; + + return Ok(()); + } + + call_script( + shell, + args.forge_args.clone(), + &DEPLOY_AND_BRIDGE_ZK_TOKEN_INTERFACE + .encode( + "supplyEraWallet", + ( + chain_config.get_wallets_config()?.governor.address, + U256::from_dec_str("10000000000000000000").unwrap(), + ), + ) + .unwrap(), + &ecosystem_config, + chain_config.get_wallets_config()?.governor_private_key(), + l1_url.clone(), + ) + .await; + + println!("Pausing for 20 seconds after funding wallet..."); + tokio::time::sleep(tokio::time::Duration::from_secs(20)).await; + println!("Resuming execution"); + + // Call the `finalizeZkTokenWithdrawal` function using the extracted parameters + let calldata = DEPLOY_AND_BRIDGE_ZK_TOKEN_INTERFACE + .encode("run", ()) + .unwrap(); + + call_script_era( + shell, + args.forge_args.clone(), + &calldata, + &ecosystem_config, + chain_config.get_wallets_config()?.governor_private_key(), + era_provider.url().to_string(), + ) + .await; + + println!("ZK Token Deployed and Withdrawn to L1!"); + + println!("Pausing for 20 seconds after withdrawing to L1..."); + tokio::time::sleep(tokio::time::Duration::from_secs(20)).await; + println!("Resuming execution"); + + let directory = "contracts/l1-contracts/broadcast/DeployZKAndBridgeToL1.s.sol/271/"; + let file_path = find_latest_json_file(directory).ok_or(anyhow::anyhow!( + "No file with `-latest.json` suffix found in the directory" + ))?; + let file = File::open(file_path)?; + let reader = BufReader::new(file); + let data: JsonData = serde_json::from_reader(reader)?; + let tx_hash = if let Some(last_transaction) = data.transactions.last() { + H256::from_slice(&hex::decode(&last_transaction.hash)?) + } else { + anyhow::bail!("No transactions found in the file."); + }; + + // Finalizing transaction + println!("Withdrawal hash: {}", hex::encode(tx_hash)); + await_for_tx_to_complete(&era_provider, tx_hash).await?; + await_for_withdrawal_to_finalize(&era_client, tx_hash).await?; + + // Fetch the parameters for calling `finalizeZkTokenWithdrawal` + let params = era_client + .get_finalize_withdrawal_params(tx_hash, 0) + .await?; + + let calldata = DEPLOY_AND_BRIDGE_ZK_TOKEN_INTERFACE + .encode( + "finalizeZkTokenWithdrawal", + ( + era_chain_id, + U256::from(params.l2_batch_number.0[0]), + U256::from(params.l2_message_index.0[0]), + U256::from(params.l2_tx_number_in_block.0[0] as u16), + params.message, + params.proof.proof, + ), + ) + .unwrap(); + + call_script( + shell, + args.forge_args.clone(), + &calldata, + &ecosystem_config, + chain_config.get_wallets_config()?.governor_private_key(), + l1_url.clone(), + ) + .await; + + println!("ZK Token withdrawal finalization started!"); + + let calldata = DEPLOY_AND_BRIDGE_ZK_TOKEN_INTERFACE + .encode("saveL1Address", ()) + .unwrap(); + + call_script( + shell, + args.forge_args, + &calldata, + &ecosystem_config, + chain_config.get_wallets_config()?.governor_private_key(), + l1_url, + ) + .await; + + println!("ZK Token L1 address saved!"); + + Ok(()) +} + +async fn call_script( + shell: &Shell, + forge_args: ForgeScriptArgs, + data: &Bytes, + config: &EcosystemConfig, + private_key: Option, + rpc_url: String, +) { + let mut forge = Forge::new(&config.path_to_l1_foundry()) + .script(&ZK_PREPARATION.script(), forge_args.clone()) + .with_ffi() + .with_rpc_url(rpc_url) + .with_broadcast() + .with_calldata(data); + + // Governor private key is required for this script + forge = fill_forge_private_key(forge, private_key).expect("private key signing failed"); + let _ = check_the_balance(&forge).await; + let _ = forge.run(shell); +} + +async fn call_script_era( + shell: &Shell, + forge_args: ForgeScriptArgs, + data: &Bytes, + config: &EcosystemConfig, + private_key: Option, + rpc_url: String, +) { + let mut forge = Forge::new(&config.path_to_l1_foundry()) + .script(&ZK_PREPARATION.script(), forge_args.clone()) + .with_ffi() + .with_zksync() + .with_rpc_url(rpc_url) + .with_broadcast() + .with_slow() + .with_calldata(data); + + // Governor private key is required for this script + forge = fill_forge_private_key(forge, private_key).expect("private key signing failed"); + let _ = check_the_balance(&forge).await; + let _ = forge.run(shell); +} + +async fn await_for_tx_to_complete(l2_provider: &Provider, hash: H256) -> anyhow::Result<()> { + println!("Waiting for transaction to complete..."); + while Middleware::get_transaction_receipt(l2_provider, hash) + .await? + .is_none() + { + tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + } + + // We do not handle network errors + let receipt = Middleware::get_transaction_receipt(l2_provider, hash) + .await? + .unwrap(); + + if receipt.status == Some(U64::from(1)) { + println!("Transaction completed successfully!"); + } else { + panic!("Transaction failed!"); + } + + Ok(()) +} + +async fn await_for_withdrawal_to_finalize( + l2_provider: &Client, + hash: H256, +) -> anyhow::Result<()> { + println!("Waiting for withdrawal to finalize..."); + while l2_provider.get_withdrawal_log(hash, 0).await.is_err() { + println!("Waiting for withdrawal to finalize..."); + tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + } + Ok(()) +} diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs index 4846ac5e891..642074ee777 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs @@ -3,6 +3,7 @@ use args::build_transactions::BuildTransactionsArgs; pub(crate) use args::create::ChainCreateArgsFinal; use clap::{command, Subcommand}; pub(crate) use create::create_chain_inner; +use deploy_and_bridge_zk::DeployAndBridgeZKArgs; use migrate_from_gateway::MigrateFromGatewayArgs; use migrate_to_gateway::MigrateToGatewayArgs; use xshell::Shell; @@ -18,6 +19,7 @@ mod build_transactions; mod common; mod convert_to_gateway; mod create; +mod deploy_and_bridge_zk; pub mod deploy_l2_contracts; pub mod deploy_paymaster; pub mod genesis; @@ -75,6 +77,8 @@ pub enum ChainCommands { MigrateToGateway(MigrateToGatewayArgs), /// Migrate chain from gateway MigrateFromGateway(MigrateFromGatewayArgs), + /// Deploy ZK token on Era and bridge it to L1 + DeployAndBridgeZK(DeployAndBridgeZKArgs), } pub(crate) async fn run(shell: &Shell, args: ChainCommands) -> anyhow::Result<()> { @@ -107,5 +111,6 @@ pub(crate) async fn run(shell: &Shell, args: ChainCommands) -> anyhow::Result<() ChainCommands::ConvertToGateway(args) => convert_to_gateway::run(args, shell).await, ChainCommands::MigrateToGateway(args) => migrate_to_gateway::run(args, shell).await, ChainCommands::MigrateFromGateway(args) => migrate_from_gateway::run(args, shell).await, + ChainCommands::DeployAndBridgeZK(args) => deploy_and_bridge_zk::run(args, shell).await, } }