Skip to content

Commit

Permalink
feat(zk_toolbox): Minting base token (#2571)
Browse files Browse the repository at this point in the history
## What ❔

This pr helps to work with custom base token: 
1. It adds an ability to choose the token from one of the predeployed
tokens
2. It mints this token to the governor (if applicable) 

## Why ❔

Simplify the work with custom base token

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] Code has been formatted via `zk fmt` and `zk lint`.

---------

Signed-off-by: Danil <[email protected]>
  • Loading branch information
Deniallugo authored Aug 13, 2024
1 parent a6213ef commit ae2dd3b
Show file tree
Hide file tree
Showing 16 changed files with 172 additions and 108 deletions.
28 changes: 0 additions & 28 deletions core/tests/ts-integration/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ async function loadTestEnvironmentFromFile(chain: string): Promise<TestEnvironme
token = Object.values(tokens.tokens)[1];
}
}
const weth = tokens.tokens['WETH'];
let baseToken;

for (const key in tokens.tokens) {
Expand All @@ -114,12 +113,6 @@ async function loadTestEnvironmentFromFile(chain: string): Promise<TestEnvironme
ethers.getDefaultProvider(l1NodeUrl)
).l2TokenAddress(token.address);

const l2WethAddress = await new zksync.Wallet(
mainWalletPK,
l2Provider,
ethers.getDefaultProvider(l1NodeUrl)
).l2TokenAddress(weth.address);

const baseTokenAddressL2 = L2_BASE_TOKEN_ADDRESS;
const l2ChainId = BigInt(genesisConfig.l2_chain_id);
const l1BatchCommitDataGeneratorMode = genesisConfig.l1_batch_commit_data_generator_mode as DataAvailabityMode;
Expand Down Expand Up @@ -152,13 +145,6 @@ async function loadTestEnvironmentFromFile(chain: string): Promise<TestEnvironme
l1Address: token.address,
l2Address: l2TokenAddress
},
wethToken: {
name: weth.name,
symbol: weth.symbol,
decimals: weth.decimals,
l1Address: weth.address,
l2Address: l2WethAddress
},
baseToken: {
name: baseToken?.name || token.name,
symbol: baseToken?.symbol || token.symbol,
Expand Down Expand Up @@ -213,7 +199,6 @@ export async function loadTestEnvironmentFromEnv(): Promise<TestEnvironment> {
if (!token) {
token = tokens[0];
}
const weth = tokens.find((token: { symbol: string }) => token.symbol == 'WETH')!;
const baseToken = tokens.find((token: { address: string }) =>
zksync.utils.isAddressEq(token.address, baseTokenAddress)
)!;
Expand All @@ -225,12 +210,6 @@ export async function loadTestEnvironmentFromEnv(): Promise<TestEnvironment> {
ethers.getDefaultProvider(l1NodeUrl)
).l2TokenAddress(token.address);

const l2WethAddress = await new zksync.Wallet(
mainWalletPK,
l2Provider,
ethers.getDefaultProvider(l1NodeUrl)
).l2TokenAddress(weth.address);

const baseTokenAddressL2 = L2_BASE_TOKEN_ADDRESS;
const l2ChainId = BigInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!);
// If the `CHAIN_STATE_KEEPER_L1_BATCH_COMMIT_DATA_GENERATOR_MODE` is not set, the default value is `Rollup`.
Expand Down Expand Up @@ -280,13 +259,6 @@ export async function loadTestEnvironmentFromEnv(): Promise<TestEnvironment> {
l1Address: token.address,
l2Address: l2TokenAddress
},
wethToken: {
name: weth.name,
symbol: weth.symbol,
decimals: weth.decimals,
l1Address: weth.address,
l2Address: l2WethAddress
},
baseToken: {
name: baseToken?.name || token.name,
symbol: baseToken?.symbol || token.symbol,
Expand Down
4 changes: 0 additions & 4 deletions core/tests/ts-integration/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,6 @@ export interface TestEnvironment {
* Description of the "main" ERC20 token used in the tests.
*/
erc20Token: Token;
/**
* Description of the WETH token used in the tests.
*/
wethToken: Token;
/**
* Description of the "base" ERC20 token used in the tests.
*/
Expand Down
40 changes: 39 additions & 1 deletion zk_toolbox/crates/common/src/ethereum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{ops::Add, time::Duration};
use std::{ops::Add, sync::Arc, time::Duration};

use ethers::{
contract::abigen,
core::k256::ecdsa::SigningKey,
middleware::MiddlewareBuilder,
prelude::{Http, LocalWallet, Provider, Signer, SignerMiddleware},
Expand Down Expand Up @@ -53,3 +54,40 @@ pub async fn distribute_eth(
futures::future::join_all(pending_txs).await;
Ok(())
}

abigen!(
TokenContract,
r"[
function mint(address to, uint256 amount)
]"
);

pub async fn mint_token(
main_wallet: Wallet,
token_address: Address,
addresses: Vec<Address>,
l1_rpc: String,
chain_id: u64,
amount: u128,
) -> anyhow::Result<()> {
let client = Arc::new(create_ethers_client(
main_wallet.private_key.unwrap(),
l1_rpc,
Some(chain_id),
)?);

let contract = TokenContract::new(token_address, client);
// contract
for address in addresses {
contract
.mint(address, amount.into())
.send()
.await?
// It's safe to set such low number of confirmations and low interval for localhost
.confirmations(1)
.interval(Duration::from_millis(30))
.await?;
}

Ok(())
}
14 changes: 11 additions & 3 deletions zk_toolbox/crates/config/src/ecosystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ use zksync_basic_types::L2ChainId;
use crate::{
consts::{
CONFIGS_PATH, CONFIG_NAME, CONTRACTS_FILE, ECOSYSTEM_PATH, ERA_CHAIN_ID,
ERC20_DEPLOYMENT_FILE, INITIAL_DEPLOYMENT_FILE, L1_CONTRACTS_FOUNDRY, LOCAL_DB_PATH,
WALLETS_FILE,
ERC20_CONFIGS_FILE, ERC20_DEPLOYMENT_FILE, INITIAL_DEPLOYMENT_FILE, L1_CONTRACTS_FOUNDRY,
LOCAL_DB_PATH, WALLETS_FILE,
},
create_localhost_wallets,
forge_interface::deploy_ecosystem::input::{Erc20DeploymentConfig, InitialDeploymentConfig},
forge_interface::deploy_ecosystem::{
input::{Erc20DeploymentConfig, InitialDeploymentConfig},
output::{ERC20Tokens, Erc20Token},
},
traits::{FileConfigWithDefaultName, ReadConfig, SaveConfig, ZkToolboxConfig},
ChainConfig, ChainConfigInternal, ContractsConfig, WalletsConfig,
};
Expand Down Expand Up @@ -169,6 +172,11 @@ impl EcosystemConfig {
pub fn get_erc20_deployment_config(&self) -> anyhow::Result<Erc20DeploymentConfig> {
Erc20DeploymentConfig::read(self.get_shell(), self.config.join(ERC20_DEPLOYMENT_FILE))
}
pub fn get_erc20_tokens(&self) -> Vec<Erc20Token> {
ERC20Tokens::read(self.get_shell(), self.config.join(ERC20_CONFIGS_FILE))
.map(|tokens| tokens.tokens.values().cloned().collect())
.unwrap_or_default()
}

pub fn get_wallets(&self) -> anyhow::Result<WalletsConfig> {
let path = self.config.join(WALLETS_FILE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,6 @@ impl Default for Erc20DeploymentConfig {
implementation: String::from("TestnetERC20Token.sol"),
mint: U256::from_str("9000000000000000000000").unwrap(),
},
Erc20DeploymentTokensConfig {
name: String::from("Wrapped Ether"),
symbol: String::from("WETH"),
decimals: 18,
implementation: String::from("WETH9.sol"),
mint: U256::zero(),
},
],
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub struct L1StateTransitionOutput {
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct TokenDeployErc20Output {
pub struct Erc20Token {
pub address: Address,
pub name: String,
pub symbol: String,
Expand All @@ -89,12 +89,12 @@ pub struct TokenDeployErc20Output {
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct DeployErc20Output {
pub tokens: HashMap<String, TokenDeployErc20Output>,
pub struct ERC20Tokens {
pub tokens: HashMap<String, Erc20Token>,
}

impl FileConfigWithDefaultName for DeployErc20Output {
impl FileConfigWithDefaultName for ERC20Tokens {
const FILE_NAME: &'static str = ERC20_CONFIGS_FILE;
}

impl ZkToolboxConfig for DeployErc20Output {}
impl ZkToolboxConfig for ERC20Tokens {}
6 changes: 6 additions & 0 deletions zk_toolbox/crates/config/src/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{
pub struct RocksDbs {
pub state_keeper: PathBuf,
pub merkle_tree: PathBuf,
pub protective_reads: PathBuf,
}

pub fn set_rocks_db_config(config: &mut GeneralConfig, rocks_dbs: RocksDbs) -> anyhow::Result<()> {
Expand All @@ -28,6 +29,11 @@ pub fn set_rocks_db_config(config: &mut GeneralConfig, rocks_dbs: RocksDbs) -> a
.context("DB config is not presented")?
.merkle_tree
.path = rocks_dbs.merkle_tree.to_str().unwrap().to_string();
config
.protective_reads_writer_config
.as_mut()
.context("Protective reads config is not presented")?
.db_path = rocks_dbs.protective_reads.to_str().unwrap().to_string();
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion zk_toolbox/crates/types/src/base_token.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ethers::types::Address;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct BaseToken {
pub address: Address,
pub nominator: u64,
Expand Down
26 changes: 19 additions & 7 deletions zk_toolbox/crates/zk_inception/src/commands/chain/args/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{path::PathBuf, str::FromStr};
use anyhow::{bail, Context};
use clap::{Parser, ValueEnum};
use common::{Prompt, PromptConfirm, PromptSelect};
use config::forge_interface::deploy_ecosystem::output::Erc20Token;
use serde::{Deserialize, Serialize};
use slugify_rs::slugify;
use strum::{Display, EnumIter, IntoEnumIterator};
Expand Down Expand Up @@ -71,6 +72,7 @@ impl ChainCreateArgs {
self,
number_of_chains: u32,
l1_network: &L1Network,
possible_erc20: Vec<Erc20Token>,
) -> anyhow::Result<ChainCreateArgsFinal> {
let mut chain_name = self
.chain_name
Expand Down Expand Up @@ -151,14 +153,24 @@ impl ChainCreateArgs {
&& self.base_token_price_denominator.is_none()
&& self.base_token_price_nominator.is_none()
{
let base_token_selection =
PromptSelect::new(MSG_BASE_TOKEN_SELECTION_PROMPT, BaseTokenSelection::iter())
.ask();
let mut token_selection: Vec<_> =
BaseTokenSelection::iter().map(|a| a.to_string()).collect();

match base_token_selection {
BaseTokenSelection::Eth => BaseToken::eth(),
BaseTokenSelection::Custom => {
let address = Prompt::new(MSG_BASE_TOKEN_ADDRESS_PROMPT).ask();
let erc20_tokens = &mut (possible_erc20
.iter()
.map(|t| format!("{:?}", t.address))
.collect());
token_selection.append(erc20_tokens);
let base_token_selection =
PromptSelect::new(MSG_BASE_TOKEN_SELECTION_PROMPT, token_selection).ask();
match base_token_selection.as_str() {
"Eth" => BaseToken::eth(),
other => {
let address = if other == "Custom" {
Prompt::new(MSG_BASE_TOKEN_ADDRESS_PROMPT).ask()
} else {
H160::from_str(other)?
};
let nominator = Prompt::new(MSG_BASE_TOKEN_PRICE_NOMINATOR_PROMPT)
.validate_with(number_validator)
.ask();
Expand Down
2 changes: 2 additions & 0 deletions zk_toolbox/crates/zk_inception/src/commands/chain/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ fn create(
ecosystem_config: &mut EcosystemConfig,
shell: &Shell,
) -> anyhow::Result<()> {
let tokens = ecosystem_config.get_erc20_tokens();
let args = args
.fill_values_with_prompt(
ecosystem_config.list_of_chains().len() as u32,
&ecosystem_config.l1_network,
tokens,
)
.context(MSG_ARGS_VALIDATOR_ERR)?;

Expand Down
78 changes: 70 additions & 8 deletions zk_toolbox/crates/zk_inception/src/commands/chain/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use config::{
traits::{ReadConfig, SaveConfig, SaveConfigWithBasePath},
update_from_chain_config, ChainConfig, ContractsConfig, EcosystemConfig,
};
use types::{BaseToken, L1Network, WalletCreation};
use xshell::Shell;

use crate::{
Expand All @@ -24,10 +25,11 @@ use crate::{
deploy_l2_contracts, deploy_paymaster,
genesis::genesis,
},
consts::AMOUNT_FOR_DISTRIBUTION_TO_WALLETS,
messages::{
msg_initializing_chain, MSG_ACCEPTING_ADMIN_SPINNER, MSG_CHAIN_INITIALIZED,
MSG_CHAIN_NOT_FOUND_ERR, MSG_GENESIS_DATABASE_ERR, MSG_REGISTERING_CHAIN_SPINNER,
MSG_SELECTED_CONFIG,
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,
},
utils::forge::{check_the_balance, fill_forge_private_key},
};
Expand Down Expand Up @@ -67,12 +69,9 @@ pub async fn init(
contracts_config.l1.base_token_addr = chain_config.base_token.address;
contracts_config.save_with_base_path(shell, &chain_config.configs)?;

crate::commands::ecosystem::init::distribute_eth(
ecosystem_config,
chain_config,
init_args.l1_rpc_url.clone(),
)
.await?;
distribute_eth(ecosystem_config, chain_config, init_args.l1_rpc_url.clone()).await?;
mint_base_token(ecosystem_config, chain_config, init_args.l1_rpc_url.clone()).await?;

let mut secrets = chain_config.get_secrets_config()?;
set_l1_rpc_url(&mut secrets, init_args.l1_rpc_url.clone())?;
secrets.save_with_base_path(shell, &chain_config.configs)?;
Expand Down Expand Up @@ -160,3 +159,66 @@ async fn register_chain(
contracts.set_chain_contracts(&register_chain_output);
Ok(())
}

// Distribute eth to the chain wallets for localhost environment
pub async fn distribute_eth(
ecosystem_config: &EcosystemConfig,
chain_config: &ChainConfig,
l1_rpc_url: String,
) -> anyhow::Result<()> {
if chain_config.wallet_creation == WalletCreation::Localhost
&& ecosystem_config.l1_network == L1Network::Localhost
{
let spinner = Spinner::new(MSG_DISTRIBUTING_ETH_SPINNER);
let wallets = ecosystem_config.get_wallets()?;
let chain_wallets = chain_config.get_wallets_config()?;
let mut addresses = vec![
chain_wallets.operator.address,
chain_wallets.blob_operator.address,
chain_wallets.governor.address,
];
if let Some(deployer) = chain_wallets.deployer {
addresses.push(deployer.address)
}
common::ethereum::distribute_eth(
wallets.operator,
addresses,
l1_rpc_url,
ecosystem_config.l1_network.chain_id(),
AMOUNT_FOR_DISTRIBUTION_TO_WALLETS,
)
.await?;
spinner.finish();
}
Ok(())
}

pub async fn mint_base_token(
ecosystem_config: &EcosystemConfig,
chain_config: &ChainConfig,
l1_rpc_url: String,
) -> anyhow::Result<()> {
if chain_config.wallet_creation == WalletCreation::Localhost
&& ecosystem_config.l1_network == L1Network::Localhost
&& chain_config.base_token != BaseToken::eth()
{
let spinner = Spinner::new(MSG_MINT_BASE_TOKEN_SPINNER);
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 amount = AMOUNT_FOR_DISTRIBUTION_TO_WALLETS * base_token.nominator as u128
/ base_token.denominator as u128;
common::ethereum::mint_token(
wallets.operator,
base_token.address,
addresses,
l1_rpc_url,
ecosystem_config.l1_network.chain_id(),
amount,
)
.await?;
spinner.finish();
}
Ok(())
}
Loading

0 comments on commit ae2dd3b

Please sign in to comment.