From b91f1684fbc159410bd6e310ebf961ab46032e56 Mon Sep 17 00:00:00 2001 From: Francisco Krause Arnim Date: Tue, 19 Dec 2023 12:39:22 -0300 Subject: [PATCH 01/17] Add tool versions for nodejs --- .tool-versions | 1 + 1 file changed, 1 insertion(+) create mode 100644 .tool-versions diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000000..8f2e342a241 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 18.18.0 From 27276a5d0f37a00608ad7225349123826c4d52ff Mon Sep 17 00:00:00 2001 From: Francisco Krause Arnim Date: Wed, 20 Dec 2023 18:29:01 -0300 Subject: [PATCH 02/17] Working ERC20 fetcher draft --- .../src/l1_gas_price/gas_adjuster/mod.rs | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs index 9a8825190ee..e1956428a0e 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs @@ -1,5 +1,6 @@ //! This module determines the fees to pay in txs containing blocks submitted to the L1. +use ::metrics::atomics::AtomicU64; use tokio::sync::watch; use std::{ @@ -18,6 +19,64 @@ mod tests; use self::metrics::METRICS; use super::{L1GasPriceProvider, L1TxParamsProvider}; +use serde::Deserialize; +use serde::Serialize; +#[derive(Deserialize, Serialize, Debug)] +struct EthValue { + eth: serde_json::value::Number, +} +#[derive(Deserialize, Serialize, Debug)] +struct Request { + dai: EthValue, +} + +/// Dedicated tether <-> eth value fetcher. +#[derive(Debug)] +struct ERC20Fetcher { + value: u64, + url: String, +} + +impl ERC20Fetcher { + async fn new() -> Self { + let url = "https://api.coingecko.com/api/v3/simple/price?x_cg_demo_api_key=CG-FEgodj8AJN55Va4c6uKPUWLe&ids=dai&vs_currencies=eth".to_string(); + let value = Self::fetch_it(&url).await; + Self { + value: Self::erc20_value_in_wei(&value), + url, + } + } + + async fn fetch_it(url: &str) -> String { + let response = reqwest::get(url) + .await + .expect("Failed request for ERC-20") + .json::() + .await + .unwrap(); + return response.dai.eth.to_string(); + } + + fn erc20_value_in_wei(value: &str) -> u64 { + let vec: Vec<&str> = value.split(".").collect(); + let whole_part: u64 = u64::from_str_radix(vec.first().unwrap(), 10).unwrap(); + let whole_part_to_wei = Self::to_wei(whole_part, 0_u32); + let decimal_length = vec.last().unwrap().len() as u32; + let decimal_part = u64::from_str_radix(vec.last().unwrap(), 10).unwrap(); + let decimal_part_to_wei = Self::to_wei(decimal_part, decimal_length); + return whole_part_to_wei + decimal_part_to_wei; + } + + fn to_wei(in_eth: u64, modifier: u32) -> u64 { + in_eth * 10_u64.pow(18_u32 - modifier) + } + async fn update_value(&mut self) { + let new_value = Self::fetch_it(&self.url).await; + + self.value = Self::erc20_value_in_wei(&new_value); + } +} + /// This component keeps track of the median base_fee from the last `max_base_fee_samples` blocks. /// It is used to adjust the base_fee of transactions sent to L1. #[derive(Debug)] @@ -25,6 +84,8 @@ pub struct GasAdjuster { pub(super) statistics: GasStatistics, pub(super) config: GasAdjusterConfig, eth_client: E, + erc_20_fetcher: ERC20Fetcher, + erc_20_value_in_wei: AtomicU64, } impl GasAdjuster { @@ -40,10 +101,14 @@ impl GasAdjuster { let history = eth_client .base_fee_history(current_block, config.max_base_fee_samples, "gas_adjuster") .await?; + let erc_20_fetcher = ERC20Fetcher::new().await; + let erc_20_value = erc_20_fetcher.value.clone(); Ok(Self { statistics: GasStatistics::new(config.max_base_fee_samples, current_block, &history), eth_client, config, + erc_20_fetcher, + erc_20_value_in_wei: AtomicU64::new(erc_20_value), }) } @@ -78,9 +143,28 @@ impl GasAdjuster { .set(*history.last().unwrap()); self.statistics.add_samples(&history); } + let new_value = + ERC20Fetcher::fetch_it("https://api.coingecko.com/api/v3/simple/price?x_cg_demo_api_key=CG-FEgodj8AJN55Va4c6uKPUWLe&ids=dai&vs_currencies=eth").await; + println!("Dai value in eth: {}", new_value); + println!( + "Dai value in wei: {}", + ERC20Fetcher::erc20_value_in_wei(&new_value) + ); + self.erc_20_value_in_wei.store( + ERC20Fetcher::erc20_value_in_wei(&new_value), + std::sync::atomic::Ordering::Relaxed, + ); + println!("Gas price {}", self.estimate_effective_gas_price()); + println!("Price in erc 20: {}", self.erc_20_gas_price()); Ok(()) } + pub fn erc_20_gas_price(&self) -> u64 { + self.erc_20_value_in_wei + .load(std::sync::atomic::Ordering::Relaxed) + / self.estimate_effective_gas_price() + } + pub async fn run(self: Arc, stop_receiver: watch::Receiver) -> anyhow::Result<()> { loop { if *stop_receiver.borrow() { From f5e431ad62a4c896dfe5c70bb59e62e72e11a706 Mon Sep 17 00:00:00 2001 From: Francisco Krause Arnim Date: Fri, 22 Dec 2023 17:29:53 -0300 Subject: [PATCH 03/17] Move code to separate file --- .../gas_adjuster/bounded_gas_adjuster.rs | 3 + .../gas_adjuster/erc_20_fetcher.rs | 54 ++++++++++ .../src/l1_gas_price/gas_adjuster/mod.rs | 98 +++---------------- .../src/l1_gas_price/main_node_fetcher.rs | 3 + core/lib/zksync_core/src/l1_gas_price/mod.rs | 2 + 5 files changed, 78 insertions(+), 82 deletions(-) create mode 100644 core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/bounded_gas_adjuster.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/bounded_gas_adjuster.rs index a4e949c4704..81a215e5ea5 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/bounded_gas_adjuster.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/bounded_gas_adjuster.rs @@ -40,4 +40,7 @@ impl L1GasPriceProvider for BoundedGasAdjuster { } default_gas_price } + fn estimate_erc_20_gas_price(&self) -> u64 { + self.default_gas_adjuster.estimate_erc_20_gas_price() + } } diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs new file mode 100644 index 00000000000..8a4e2b016af --- /dev/null +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs @@ -0,0 +1,54 @@ +use serde::Deserialize; +use serde::Serialize; +use zksync_eth_client::types::Error; +#[derive(Deserialize, Serialize, Debug)] +struct EthValue { + eth: serde_json::value::Number, +} +#[derive(Deserialize, Serialize, Debug)] +struct Request { + dai: EthValue, +} +/// TODO: This is for an easy refactor to test things, +/// and have a POC. +/// Let's discuss where this should actually be. +async fn fetch_it() -> Result { + let url = + "https://api.coingecko.com/api/v3/simple/price?x_cg_demo_api_key=CG-FEgodj8AJN55Va4c6uKPUWLe&ids=dai&vs_currencies=eth"; + let response = reqwest::get(url) + .await + .expect("Failed request for ERC-20") + .json::() + .await + .unwrap(); + Ok(response.dai.eth.to_string()) +} + +fn erc20_value_from_eth_to_wei(value_in_eth: &str) -> Result { + let vec: Vec<&str> = value_in_eth.split(".").collect(); + let whole_part: u64 = u64::from_str_radix( + vec.first() + .ok_or("Expected decimal value separated by coma")?, + 10, + ) + .map_err(|_| "Expected decimal value separated by coma")?; + let whole_part_in_wei = to_wei(whole_part, 0_u32); + let decimal_length = vec.last().unwrap().len() as u32; + let decimal_part = u64::from_str_radix( + vec.last() + .ok_or("Expected decimal value separated by coma")?, + 10, + ) + .map_err(|_| "Expected decimal value separated by coma")?; + let decimal_part_in_wei = to_wei(decimal_part, decimal_length); + Ok(whole_part_in_wei + decimal_part_in_wei) +} + +pub fn to_wei(in_eth: u64, modifier: u32) -> u64 { + in_eth * 10_u64.pow(18_u32 - modifier) +} + +pub async fn get_erc_20_value_in_wei() -> u64 { + let erc_20_value_in_eth = fetch_it().await.unwrap(); + erc20_value_from_eth_to_wei(&erc_20_value_in_eth).unwrap() +} diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs index e1956428a0e..e2a4990522d 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs @@ -12,71 +12,13 @@ use zksync_config::GasAdjusterConfig; use zksync_eth_client::{types::Error, EthInterface}; pub mod bounded_gas_adjuster; +mod erc_20_fetcher; mod metrics; #[cfg(test)] mod tests; - -use self::metrics::METRICS; +use self::{erc_20_fetcher::get_erc_20_value_in_wei, metrics::METRICS}; use super::{L1GasPriceProvider, L1TxParamsProvider}; -use serde::Deserialize; -use serde::Serialize; -#[derive(Deserialize, Serialize, Debug)] -struct EthValue { - eth: serde_json::value::Number, -} -#[derive(Deserialize, Serialize, Debug)] -struct Request { - dai: EthValue, -} - -/// Dedicated tether <-> eth value fetcher. -#[derive(Debug)] -struct ERC20Fetcher { - value: u64, - url: String, -} - -impl ERC20Fetcher { - async fn new() -> Self { - let url = "https://api.coingecko.com/api/v3/simple/price?x_cg_demo_api_key=CG-FEgodj8AJN55Va4c6uKPUWLe&ids=dai&vs_currencies=eth".to_string(); - let value = Self::fetch_it(&url).await; - Self { - value: Self::erc20_value_in_wei(&value), - url, - } - } - - async fn fetch_it(url: &str) -> String { - let response = reqwest::get(url) - .await - .expect("Failed request for ERC-20") - .json::() - .await - .unwrap(); - return response.dai.eth.to_string(); - } - - fn erc20_value_in_wei(value: &str) -> u64 { - let vec: Vec<&str> = value.split(".").collect(); - let whole_part: u64 = u64::from_str_radix(vec.first().unwrap(), 10).unwrap(); - let whole_part_to_wei = Self::to_wei(whole_part, 0_u32); - let decimal_length = vec.last().unwrap().len() as u32; - let decimal_part = u64::from_str_radix(vec.last().unwrap(), 10).unwrap(); - let decimal_part_to_wei = Self::to_wei(decimal_part, decimal_length); - return whole_part_to_wei + decimal_part_to_wei; - } - - fn to_wei(in_eth: u64, modifier: u32) -> u64 { - in_eth * 10_u64.pow(18_u32 - modifier) - } - async fn update_value(&mut self) { - let new_value = Self::fetch_it(&self.url).await; - - self.value = Self::erc20_value_in_wei(&new_value); - } -} - /// This component keeps track of the median base_fee from the last `max_base_fee_samples` blocks. /// It is used to adjust the base_fee of transactions sent to L1. #[derive(Debug)] @@ -84,7 +26,6 @@ pub struct GasAdjuster { pub(super) statistics: GasStatistics, pub(super) config: GasAdjusterConfig, eth_client: E, - erc_20_fetcher: ERC20Fetcher, erc_20_value_in_wei: AtomicU64, } @@ -101,14 +42,11 @@ impl GasAdjuster { let history = eth_client .base_fee_history(current_block, config.max_base_fee_samples, "gas_adjuster") .await?; - let erc_20_fetcher = ERC20Fetcher::new().await; - let erc_20_value = erc_20_fetcher.value.clone(); Ok(Self { statistics: GasStatistics::new(config.max_base_fee_samples, current_block, &history), eth_client, config, - erc_20_fetcher, - erc_20_value_in_wei: AtomicU64::new(erc_20_value), + erc_20_value_in_wei: AtomicU64::new(get_erc_20_value_in_wei().await), }) } @@ -143,26 +81,13 @@ impl GasAdjuster { .set(*history.last().unwrap()); self.statistics.add_samples(&history); } - let new_value = - ERC20Fetcher::fetch_it("https://api.coingecko.com/api/v3/simple/price?x_cg_demo_api_key=CG-FEgodj8AJN55Va4c6uKPUWLe&ids=dai&vs_currencies=eth").await; - println!("Dai value in eth: {}", new_value); - println!( - "Dai value in wei: {}", - ERC20Fetcher::erc20_value_in_wei(&new_value) - ); + self.erc_20_value_in_wei.store( - ERC20Fetcher::erc20_value_in_wei(&new_value), + erc_20_fetcher::get_erc_20_value_in_wei().await, std::sync::atomic::Ordering::Relaxed, ); - println!("Gas price {}", self.estimate_effective_gas_price()); - println!("Price in erc 20: {}", self.erc_20_gas_price()); - Ok(()) - } - pub fn erc_20_gas_price(&self) -> u64 { - self.erc_20_value_in_wei - .load(std::sync::atomic::Ordering::Relaxed) - / self.estimate_effective_gas_price() + Ok(()) } pub async fn run(self: Arc, stop_receiver: watch::Receiver) -> anyhow::Result<()> { @@ -176,7 +101,8 @@ impl GasAdjuster { tracing::warn!("Cannot add the base fee to gas statistics: {}", err); } - tokio::time::sleep(self.config.poll_period()).await; + // tokio::time::sleep(self.config.poll_period()).await; + tokio::time::sleep(tokio::time::Duration::from_millis(100_000_000)).await; } Ok(()) } @@ -194,6 +120,14 @@ impl L1GasPriceProvider for GasAdjuster { (self.config.internal_l1_pricing_multiplier * effective_gas_price as f64) as u64 } + + /// TODO: This is for an easy refactor to test things, + /// let's discuss where this should actually be. + fn estimate_erc_20_gas_price(&self) -> u64 { + self.erc_20_value_in_wei + .load(std::sync::atomic::Ordering::Relaxed) + / self.estimate_effective_gas_price() + } } impl L1TxParamsProvider for GasAdjuster { diff --git a/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs b/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs index 2244607a47e..64df5454257 100644 --- a/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs +++ b/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs @@ -71,4 +71,7 @@ impl L1GasPriceProvider for MainNodeGasPriceFetcher { fn estimate_effective_gas_price(&self) -> u64 { self.gas_price.load(Ordering::Relaxed) } + fn estimate_erc_20_gas_price(&self) -> u64 { + return 1; + } } diff --git a/core/lib/zksync_core/src/l1_gas_price/mod.rs b/core/lib/zksync_core/src/l1_gas_price/mod.rs index 45e228d79c5..52fe9f21b04 100644 --- a/core/lib/zksync_core/src/l1_gas_price/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/mod.rs @@ -15,6 +15,8 @@ pub trait L1GasPriceProvider { /// Returns a best guess of a realistic value for the L1 gas price. /// Return value is in wei. fn estimate_effective_gas_price(&self) -> u64; + + fn estimate_erc_20_gas_price(&self) -> u64; } /// Extended version of `L1GasPriceProvider` that can provide parameters From 0eae669de21eecd8fbc1ee5036ef812b193b4514 Mon Sep 17 00:00:00 2001 From: Francisco Krause Arnim Date: Fri, 22 Dec 2023 18:01:41 -0300 Subject: [PATCH 04/17] Replace gas price with erc-20 value --- core/lib/zksync_core/src/api_server/tx_sender/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs index b994fbff882..b584336718e 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs @@ -877,7 +877,8 @@ impl TxSender { } pub fn gas_price(&self) -> u64 { - let gas_price = self.0.l1_gas_price_source.estimate_effective_gas_price(); + // let gas_price = self.0.l1_gas_price_source.estimate_effective_gas_price(); + let gas_price = self.0.l1_gas_price_source.estimate_erc_20_gas_price(); let l1_gas_price = (gas_price as f64 * self.0.sender_config.gas_price_scale_factor).round(); let (base_fee, _) = derive_base_fee_and_gas_per_pubdata( l1_gas_price as u64, From 0f77ac957615f6283d95ffb4b2baaa2c440267d5 Mon Sep 17 00:00:00 2001 From: Francisco Krause Arnim Date: Fri, 22 Dec 2023 19:08:56 -0300 Subject: [PATCH 05/17] Rename variable --- .../src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs index 8a4e2b016af..60da74af935 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs @@ -25,17 +25,19 @@ async fn fetch_it() -> Result { } fn erc20_value_from_eth_to_wei(value_in_eth: &str) -> Result { - let vec: Vec<&str> = value_in_eth.split(".").collect(); - let whole_part: u64 = u64::from_str_radix( - vec.first() + let splitted_value: Vec<&str> = value_in_eth.split(".").collect(); + let whole_part = u64::from_str_radix( + splitted_value + .first() .ok_or("Expected decimal value separated by coma")?, 10, ) .map_err(|_| "Expected decimal value separated by coma")?; let whole_part_in_wei = to_wei(whole_part, 0_u32); - let decimal_length = vec.last().unwrap().len() as u32; + let decimal_length = splitted_value.last().unwrap().len() as u32; let decimal_part = u64::from_str_radix( - vec.last() + splitted_value + .last() .ok_or("Expected decimal value separated by coma")?, 10, ) From fa232dc31fd8be8f2fc5b6728861045ebad404c9 Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Wed, 3 Jan 2024 12:44:46 -0300 Subject: [PATCH 06/17] zk fmt --- infrastructure/zk/src/init.ts | 2 +- infrastructure/zk/src/run/run.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/infrastructure/zk/src/init.ts b/infrastructure/zk/src/init.ts index f9a671837bc..f38941985c7 100644 --- a/infrastructure/zk/src/init.ts +++ b/infrastructure/zk/src/init.ts @@ -68,7 +68,7 @@ export async function init(initArgs: InitArgs = DEFAULT_ARGS) { if (nativeERC20) { await announced('Approving Proxy Contract for deployer deposits', run.approve()); - } + } await announced( 'Deploying L2 contracts', diff --git a/infrastructure/zk/src/run/run.ts b/infrastructure/zk/src/run/run.ts index 234ca44e900..5db31fb281b 100644 --- a/infrastructure/zk/src/run/run.ts +++ b/infrastructure/zk/src/run/run.ts @@ -58,13 +58,13 @@ export async function deployERC20( export async function approve() { let path = `${process.env.ZKSYNC_HOME}/etc/tokens/native_erc20.json`; - let rawData = fs.readFileSync(path, "utf8"); - let address = "0x52312AD6f01657413b2eaE9287f6B9ADaD93D5FE"; + let rawData = fs.readFileSync(path, 'utf8'); + let address = '0x52312AD6f01657413b2eaE9287f6B9ADaD93D5FE'; try { let jsonConfig = JSON.parse(rawData); address = jsonConfig.address; } catch (_e) { - address = "0x52312AD6f01657413b2eaE9287f6B9ADaD93D5FE"; + address = '0x52312AD6f01657413b2eaE9287f6B9ADaD93D5FE'; } await utils.spawn( From a01c280132fee3b7cdf341cacd8f4c23c7b2cea2 Mon Sep 17 00:00:00 2001 From: Francisco Krause Arnim Date: Tue, 19 Dec 2023 12:39:22 -0300 Subject: [PATCH 07/17] Add tool versions for nodejs --- .tool-versions | 1 + 1 file changed, 1 insertion(+) create mode 100644 .tool-versions diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000000..8f2e342a241 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 18.18.0 From c0fa6d95fabb1bc574ecba3876bab3e8cb3f311a Mon Sep 17 00:00:00 2001 From: Francisco Krause Arnim Date: Mon, 8 Jan 2024 16:55:47 -0300 Subject: [PATCH 08/17] Gas Price RPC changes --- core/lib/zksync_core/src/api_server/tx_sender/mod.rs | 6 +++--- .../src/api_server/web3/backend_jsonrpsee/namespaces/eth.rs | 4 +++- core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs | 3 +-- core/lib/zksync_core/src/l1_gas_price/mod.rs | 1 + core/lib/zksync_core/src/state_keeper/keeper.rs | 5 ++++- etc/tokens/native_erc20.json | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs index b584336718e..5779cac5eb1 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs @@ -877,14 +877,14 @@ impl TxSender { } pub fn gas_price(&self) -> u64 { - // let gas_price = self.0.l1_gas_price_source.estimate_effective_gas_price(); - let gas_price = self.0.l1_gas_price_source.estimate_erc_20_gas_price(); + let gas_price = self.0.l1_gas_price_source.estimate_effective_gas_price(); + // let gas_price = self.0.l1_gas_price_source.estimate_erc_20_gas_price(); let l1_gas_price = (gas_price as f64 * self.0.sender_config.gas_price_scale_factor).round(); let (base_fee, _) = derive_base_fee_and_gas_per_pubdata( l1_gas_price as u64, self.0.sender_config.fair_l2_gas_price, ); - base_fee + base_fee * self.0.l1_gas_price_source.estimate_erc_20_gas_price() } fn ensure_tx_executable( diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/eth.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/eth.rs index 4766b1e8878..3751673ba8e 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/eth.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/eth.rs @@ -41,7 +41,9 @@ impl EthNamespaceServer for EthNa } async fn gas_price(&self) -> RpcResult { - self.gas_price_impl().map_err(into_jsrpc_error) + let gas_price = self.gas_price_impl().map_err(into_jsrpc_error); + println!("The gas price: {:?}", gas_price); + return gas_price; } async fn new_filter(&self, filter: Filter) -> RpcResult { diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs index e2a4990522d..7b7b55919af 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs @@ -12,7 +12,7 @@ use zksync_config::GasAdjusterConfig; use zksync_eth_client::{types::Error, EthInterface}; pub mod bounded_gas_adjuster; -mod erc_20_fetcher; +pub mod erc_20_fetcher; mod metrics; #[cfg(test)] mod tests; @@ -126,7 +126,6 @@ impl L1GasPriceProvider for GasAdjuster { fn estimate_erc_20_gas_price(&self) -> u64 { self.erc_20_value_in_wei .load(std::sync::atomic::Ordering::Relaxed) - / self.estimate_effective_gas_price() } } diff --git a/core/lib/zksync_core/src/l1_gas_price/mod.rs b/core/lib/zksync_core/src/l1_gas_price/mod.rs index 52fe9f21b04..4840010eb25 100644 --- a/core/lib/zksync_core/src/l1_gas_price/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/mod.rs @@ -1,6 +1,7 @@ //! This module determines the fees to pay in txs containing blocks submitted to the L1. pub use gas_adjuster::bounded_gas_adjuster::BoundedGasAdjuster; +pub use gas_adjuster::erc_20_fetcher; pub use gas_adjuster::GasAdjuster; pub use main_node_fetcher::MainNodeGasPriceFetcher; pub use singleton::GasAdjusterSingleton; diff --git a/core/lib/zksync_core/src/state_keeper/keeper.rs b/core/lib/zksync_core/src/state_keeper/keeper.rs index 761e186e7ae..10c630e7fc8 100644 --- a/core/lib/zksync_core/src/state_keeper/keeper.rs +++ b/core/lib/zksync_core/src/state_keeper/keeper.rs @@ -138,7 +138,10 @@ impl ZkSyncStateKeeper { } } }; - + println!("Before multiplying by l2 gas price"); + l1_batch_env.fair_l2_gas_price *= + crate::l1_gas_price::erc_20_fetcher::get_erc_20_value_in_wei().await; + println!("Price of l2 gas: {}", l1_batch_env.fair_l2_gas_price); let protocol_version = system_env.version; let mut updates_manager = UpdatesManager::new( l1_batch_env.clone(), diff --git a/etc/tokens/native_erc20.json b/etc/tokens/native_erc20.json index bc72802e1c4..85f91fd170c 100644 --- a/etc/tokens/native_erc20.json +++ b/etc/tokens/native_erc20.json @@ -1,5 +1,5 @@ { - "address": "0x714FE7588649B69Fb58317e986a16676526772Ff", + "address": "0x601f6d7BE97C1B55ff1F65CE777a2701F16e07E4", "name": "lambdacoin", "symbol": "LBC", "decimals": "18" From ec1885c92bec41d4b8a0f276ec9e64985e0cbc3d Mon Sep 17 00:00:00 2001 From: Francisco Krause Arnim Date: Tue, 9 Jan 2024 15:08:38 -0300 Subject: [PATCH 09/17] Temporarily remove some debug statements for clearer logs --- core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs b/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs index 4cb40c475f9..8059eb9d7ea 100644 --- a/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs +++ b/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs @@ -309,7 +309,7 @@ impl EthTxAggregator { // This is here for backward compatibility with the old verifier: // Pre-boojum verifier returns the full verification key; // New verifier returns the hash of the verification key - tracing::debug!("Calling get_verification_key"); + // tracing::debug!("Calling get_verification_key"); if contracts_are_pre_boojum { let abi = Contract { functions: vec![( @@ -334,7 +334,7 @@ impl EthTxAggregator { Ok(l1_vk_commitment(vk)) } else { let get_vk_hash = self.functions.verification_key_hash.as_ref(); - tracing::debug!("Calling verificationKeyHash"); + // tracing::debug!("Calling verificationKeyHash"); let vk_hash = eth_client .call_contract_function( &get_vk_hash.unwrap().name, From 7d2da936c41345b288815df453d8916905732fe2 Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Wed, 10 Jan 2024 11:54:16 -0300 Subject: [PATCH 10/17] Add conversion rate RPC endpoint --- core/lib/web3_decl/src/namespaces/zks.rs | 3 +++ .../zksync_core/src/api_server/tx_sender/mod.rs | 3 +-- .../web3/backend_jsonrpc/namespaces/zks.rs | 13 +++++++++++++ .../web3/backend_jsonrpsee/namespaces/zks.rs | 6 ++++++ .../src/api_server/web3/namespaces/zks.rs | 13 ++++++++++++- .../gas_adjuster/bounded_gas_adjuster.rs | 4 ++-- .../l1_gas_price/gas_adjuster/erc_20_fetcher.rs | 5 +++-- .../src/l1_gas_price/gas_adjuster/mod.rs | 2 +- .../src/l1_gas_price/main_node_fetcher.rs | 14 +++++++++++--- core/lib/zksync_core/src/l1_gas_price/mod.rs | 2 +- core/lib/zksync_core/src/state_keeper/keeper.rs | 4 ++-- 11 files changed, 55 insertions(+), 14 deletions(-) diff --git a/core/lib/web3_decl/src/namespaces/zks.rs b/core/lib/web3_decl/src/namespaces/zks.rs index d3bf43b9a97..cb1f38f5e5e 100644 --- a/core/lib/web3_decl/src/namespaces/zks.rs +++ b/core/lib/web3_decl/src/namespaces/zks.rs @@ -118,4 +118,7 @@ pub trait ZksNamespace { keys: Vec, l1_batch_number: L1BatchNumber, ) -> RpcResult; + + #[method(name = "getConversionRate")] + async fn get_conversion_rate(&self) -> RpcResult; } diff --git a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs index 5779cac5eb1..400888fa624 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs @@ -878,13 +878,12 @@ impl TxSender { pub fn gas_price(&self) -> u64 { let gas_price = self.0.l1_gas_price_source.estimate_effective_gas_price(); - // let gas_price = self.0.l1_gas_price_source.estimate_erc_20_gas_price(); let l1_gas_price = (gas_price as f64 * self.0.sender_config.gas_price_scale_factor).round(); let (base_fee, _) = derive_base_fee_and_gas_per_pubdata( l1_gas_price as u64, self.0.sender_config.fair_l2_gas_price, ); - base_fee * self.0.l1_gas_price_source.estimate_erc_20_gas_price() + base_fee * self.0.l1_gas_price_source.get_erc20_conversion_rate() } fn ensure_tx_executable( diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/zks.rs index bf700a64156..c8866c51be0 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/zks.rs @@ -119,6 +119,9 @@ pub trait ZksNamespaceT { keys: Vec, l1_batch_number: L1BatchNumber, ) -> BoxFuture>; + + #[rpc(name = "zks_getConversionRate")] + fn get_conversion_rate(&self) -> BoxFuture>; } impl ZksNamespaceT for ZksNamespace { @@ -331,4 +334,14 @@ impl ZksNamespaceT for ZksNamespa .map_err(into_jsrpc_error) }) } + + fn get_conversion_rate(&self) -> BoxFuture> { + let self_ = self.clone(); + Box::pin(async move { + self_ + .get_conversion_rate_impl() + .await + .map_err(into_jsrpc_error) + }) + } } diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs index 6b6ed67c3c6..b7ac6cf0547 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs @@ -169,4 +169,10 @@ impl ZksNamespaceServer for ZksNa .await .map_err(into_jsrpc_error) } + + async fn get_conversion_rate(&self) -> RpcResult { + self.get_conversion_rate_impl() + .await + .map_err(into_jsrpc_error) + } } diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs index 7f38c6afc52..85cc3e64d6a 100644 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, convert::TryInto}; +use std::{collections::HashMap, convert::TryInto, str::FromStr}; use bigdecimal::{BigDecimal, Zero}; use zksync_dal::StorageProcessor; @@ -679,4 +679,15 @@ impl ZksNamespace { storage_proof, }) } + + #[tracing::instrument(skip_all)] + pub async fn get_conversion_rate_impl(&self) -> Result { + Ok(U64::from( + self.state + .tx_sender + .0 + .l1_gas_price_source + .get_erc20_conversion_rate(), + )) + } } diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/bounded_gas_adjuster.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/bounded_gas_adjuster.rs index 81a215e5ea5..1db1d638687 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/bounded_gas_adjuster.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/bounded_gas_adjuster.rs @@ -40,7 +40,7 @@ impl L1GasPriceProvider for BoundedGasAdjuster { } default_gas_price } - fn estimate_erc_20_gas_price(&self) -> u64 { - self.default_gas_adjuster.estimate_erc_20_gas_price() + fn get_erc20_conversion_rate(&self) -> u64 { + self.default_gas_adjuster.get_erc20_conversion_rate() } } diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs index 60da74af935..5a79909aa4d 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/erc_20_fetcher.rs @@ -51,6 +51,7 @@ pub fn to_wei(in_eth: u64, modifier: u32) -> u64 { } pub async fn get_erc_20_value_in_wei() -> u64 { - let erc_20_value_in_eth = fetch_it().await.unwrap(); - erc20_value_from_eth_to_wei(&erc_20_value_in_eth).unwrap() + // let erc_20_value_in_eth = fetch_it().await.unwrap(); + // erc20_value_from_eth_to_wei(&erc_20_value_in_eth).unwrap() + 11 } diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs index 7b7b55919af..b73027cdd28 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs @@ -123,7 +123,7 @@ impl L1GasPriceProvider for GasAdjuster { /// TODO: This is for an easy refactor to test things, /// let's discuss where this should actually be. - fn estimate_erc_20_gas_price(&self) -> u64 { + fn get_erc20_conversion_rate(&self) -> u64 { self.erc_20_value_in_wei .load(std::sync::atomic::Ordering::Relaxed) } diff --git a/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs b/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs index 64df5454257..7815e07a71a 100644 --- a/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs +++ b/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs @@ -13,7 +13,7 @@ use zksync_web3_decl::{ namespaces::ZksNamespaceClient, }; -use super::L1GasPriceProvider; +use super::{erc_20_fetcher, L1GasPriceProvider}; const SLEEP_INTERVAL: Duration = Duration::from_secs(5); @@ -27,6 +27,7 @@ const SLEEP_INTERVAL: Duration = Duration::from_secs(5); pub struct MainNodeGasPriceFetcher { client: HttpClient, gas_price: AtomicU64, + erc20_value_in_wei: AtomicU64, } impl MainNodeGasPriceFetcher { @@ -34,6 +35,7 @@ impl MainNodeGasPriceFetcher { Self { client: Self::build_client(main_node_url), gas_price: AtomicU64::new(1u64), // Start with 1 wei until the first update. + erc20_value_in_wei: AtomicU64::new(1u64), } } @@ -62,6 +64,11 @@ impl MainNodeGasPriceFetcher { self.gas_price .store(main_node_gas_price.as_u64(), Ordering::Relaxed); tokio::time::sleep(SLEEP_INTERVAL).await; + + self.erc20_value_in_wei.store( + erc_20_fetcher::get_erc_20_value_in_wei().await, + std::sync::atomic::Ordering::Relaxed, + ); } Ok(()) } @@ -71,7 +78,8 @@ impl L1GasPriceProvider for MainNodeGasPriceFetcher { fn estimate_effective_gas_price(&self) -> u64 { self.gas_price.load(Ordering::Relaxed) } - fn estimate_erc_20_gas_price(&self) -> u64 { - return 1; + + fn get_erc20_conversion_rate(&self) -> u64 { + self.erc20_value_in_wei.load(Ordering::Relaxed) } } diff --git a/core/lib/zksync_core/src/l1_gas_price/mod.rs b/core/lib/zksync_core/src/l1_gas_price/mod.rs index 4840010eb25..3811070d42e 100644 --- a/core/lib/zksync_core/src/l1_gas_price/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/mod.rs @@ -17,7 +17,7 @@ pub trait L1GasPriceProvider { /// Return value is in wei. fn estimate_effective_gas_price(&self) -> u64; - fn estimate_erc_20_gas_price(&self) -> u64; + fn get_erc20_conversion_rate(&self) -> u64; } /// Extended version of `L1GasPriceProvider` that can provide parameters diff --git a/core/lib/zksync_core/src/state_keeper/keeper.rs b/core/lib/zksync_core/src/state_keeper/keeper.rs index 10c630e7fc8..1ff31d62d41 100644 --- a/core/lib/zksync_core/src/state_keeper/keeper.rs +++ b/core/lib/zksync_core/src/state_keeper/keeper.rs @@ -139,8 +139,8 @@ impl ZkSyncStateKeeper { } }; println!("Before multiplying by l2 gas price"); - l1_batch_env.fair_l2_gas_price *= - crate::l1_gas_price::erc_20_fetcher::get_erc_20_value_in_wei().await; + // l1_batch_env.fair_l2_gas_price *= + // crate::l1_gas_price::erc_20_fetcher::get_erc_20_value_in_wei().await; println!("Price of l2 gas: {}", l1_batch_env.fair_l2_gas_price); let protocol_version = system_env.version; let mut updates_manager = UpdatesManager::new( From 5ba1dfbe762ba0c43b15adcca52fedc5f6cd7570 Mon Sep 17 00:00:00 2001 From: Jmunoz Date: Mon, 15 Jan 2024 18:44:06 -0300 Subject: [PATCH 11/17] initial commit --- core/tests/ts-integration/package.json | 2 +- .../tests/ts-integration/src/context-owner.ts | 95 +++---- core/tests/ts-integration/src/env.ts | 6 +- core/tests/ts-integration/src/test-master.ts | 2 +- .../ts-integration/tests/native-erc20.test.ts | 241 ++++++++++++++++++ etc/tokens/native_erc20.json | 2 +- yarn.lock | 5 + 7 files changed, 301 insertions(+), 52 deletions(-) create mode 100644 core/tests/ts-integration/tests/native-erc20.test.ts diff --git a/core/tests/ts-integration/package.json b/core/tests/ts-integration/package.json index 63b51a6e179..95423097eaf 100644 --- a/core/tests/ts-integration/package.json +++ b/core/tests/ts-integration/package.json @@ -30,6 +30,6 @@ "ts-jest": "^29.0.1", "ts-node": "^10.1.0", "typescript": "^4.3.5", - "zksync-web3": "^0.15.5" + "zksync-web3": "../zksync2-js" } } diff --git a/core/tests/ts-integration/src/context-owner.ts b/core/tests/ts-integration/src/context-owner.ts index ba22a4117b3..7bd560df45b 100644 --- a/core/tests/ts-integration/src/context-owner.ts +++ b/core/tests/ts-integration/src/context-owner.ts @@ -79,8 +79,8 @@ export class TestContextOwner { this.l2Provider.pollingInterval = 100; } - this.mainEthersWallet = new ethers.Wallet(env.mainWalletPK, this.l1Provider); - this.mainSyncWallet = new zksync.Wallet(env.mainWalletPK, this.l2Provider, this.l1Provider); + this.mainEthersWallet = new ethers.Wallet("0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705", this.l1Provider); + this.mainSyncWallet = new zksync.Wallet("0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705", this.l2Provider, this.l1Provider); } // Returns the required amount of L1 ETH @@ -124,7 +124,7 @@ export class TestContextOwner { const ethWallet = this.mainEthersWallet; const latestNonce = await ethWallet.getTransactionCount('latest'); const pendingNonce = await ethWallet.getTransactionCount('pending'); - this.reporter.debug(`Latest nonce is ${latestNonce}, pending nonce is ${pendingNonce}`); + this.reporter.message(`Latest nonce is ${latestNonce}, pending nonce is ${pendingNonce}`); const cancellationTxs = []; for (let nonce = latestNonce; nonce < pendingNonce; nonce++) { // For each transaction to override it, we need to provide greater fee. @@ -238,27 +238,26 @@ export class TestContextOwner { const gasPrice = await scaledGasPrice(this.mainEthersWallet); // Deposit L2 tokens (if needed). - if (!l2ETHAmountToDeposit.isZero()) { - // Given that we've already sent a number of transactions, - // we have to correctly send nonce. - const depositHandle = this.mainSyncWallet - .deposit({ - token: zksync.utils.ETH_ADDRESS, - amount: l2ETHAmountToDeposit, - overrides: { - nonce: nonce++, - gasPrice - } - }) - .then((tx) => { - const amount = ethers.utils.formatEther(l2ETHAmountToDeposit); - this.reporter.debug(`Sent ETH deposit. Nonce ${tx.nonce}, amount: ${amount}, hash: ${tx.hash}`); - tx.wait(); - }); - - // Add this promise to the list of L1 tx promises. - l1TxPromises.push(depositHandle); - } + // if (!l2ETHAmountToDeposit.isZero()) { + // // Given that we've already sent a number of transactions, + // // we have to correctly send nonce. + // const depositHandle = this.mainSyncWallet + // .deposit({ + // to: this.mainEthersWallet.address, + // approveERC20: true, + // token: this.env.erc20Token.l1Address, + // amount: l2ETHAmountToDeposit, + // refundRecipient: this.mainEthersWallet.address, + // }) + // .then((tx) => { + // const amount = ethers.utils.formatEther(l2ETHAmountToDeposit); + // this.reporter.debug(`Sent ETH deposit. Nonce ${tx.nonce}, amount: ${amount}, hash: ${tx.hash}`); + // tx.wait(); + // }); + + // // Add this promise to the list of L1 tx promises. + // l1TxPromises.push(depositHandle); + // } // Define values for handling ERC20 transfers/deposits. const erc20Token = this.env.erc20Token.l1Address; @@ -280,18 +279,19 @@ export class TestContextOwner { // Deposit ERC20. const erc20DepositPromise = this.mainSyncWallet .deposit({ + to: this.mainEthersWallet.address, token: erc20Token, amount: l2erc20DepositAmount, approveERC20: true, - approveOverrides: { - nonce: nonce++, - gasPrice - }, - overrides: { - nonce: nonce++, - gasPrice - } - }) + // approveOverrides: { + // nonce: nonce++, + // gasPrice + // }, + // overrides: { + // nonce: nonce++, + // gasPrice + // } + }, erc20Token) .then((tx) => { // Note: there is an `approve` tx, not listed here. this.reporter.debug(`Sent ERC20 deposit transaction. Hash: ${tx.hash}, nonce: ${tx.nonce}`); @@ -312,7 +312,7 @@ export class TestContextOwner { // Send ERC20 on L1. const erc20Transfers = await sendTransfers( - erc20Token, + this.env.erc20Token.l1Address, this.mainEthersWallet, wallets, ERC20_PER_ACCOUNT, @@ -321,12 +321,12 @@ export class TestContextOwner { this.reporter ); - l1TxPromises.push(...ethTransfers); l1TxPromises.push(erc20MintPromise); l1TxPromises.push(erc20DepositPromise); + l1TxPromises.push(...ethTransfers); l1TxPromises.push(...erc20Transfers); - this.reporter.debug(`Sent ${l1TxPromises.length} initial transactions on L1`); + // this.reporter.debug(`Sent ${l1TxPromises.length} initial transactions on L1`); await Promise.all(l1TxPromises); this.reporter.finishAction(); @@ -339,17 +339,20 @@ export class TestContextOwner { this.reporter.startAction(`Distributing tokens on L2`); let l2startNonce = await this.mainSyncWallet.getTransactionCount(); + // All the promises we send in this function. + const l2TxPromises: Promise[] = []; + // ETH transfers. - const l2TxPromises = await sendTransfers( - zksync.utils.ETH_ADDRESS, - this.mainSyncWallet, - wallets, - L2_ETH_PER_ACCOUNT, - l2startNonce, - undefined, - this.reporter - ); - l2startNonce += l2TxPromises.length; + // const l2TxPromises = await sendTransfers( + // zksync.utils.ETH_ADDRESS, + // this.mainSyncWallet, + // wallets, + // L2_ETH_PER_ACCOUNT, + // l2startNonce, + // undefined + // this.reporter + // ); + // l2startNonce += l2TxPromises.length; // ERC20 transfers. const l2TokenAddress = await this.mainSyncWallet.l2TokenAddress(this.env.erc20Token.l1Address); diff --git a/core/tests/ts-integration/src/env.ts b/core/tests/ts-integration/src/env.ts index 1f376e815d7..7a1ebe5e086 100644 --- a/core/tests/ts-integration/src/env.ts +++ b/core/tests/ts-integration/src/env.ts @@ -75,7 +75,7 @@ export async function loadTestEnvironment(): Promise { // wBTC is chosen because it has decimals different from ETH (8 instead of 18). // Using this token will help us to detect decimals-related errors. // but if it's not available, we'll use the first token from the list. - let token = tokens.find((token: { symbol: string }) => token.symbol == 'wBTC')!; + let token = tokens.find((token: { symbol: string }) => token.symbol == 'LBC')!; if (!token) { token = tokens[0]; } @@ -83,13 +83,13 @@ export async function loadTestEnvironment(): Promise { // `waitForServer` is expected to be executed. Otherwise this call may throw. const l2TokenAddress = await new zksync.Wallet( - mainWalletPK, + "0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705", new zksync.Provider(l2NodeUrl), ethers.getDefaultProvider(l1NodeUrl) ).l2TokenAddress(token.address); const l2WethAddress = await new zksync.Wallet( - mainWalletPK, + "0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705", new zksync.Provider(l2NodeUrl), ethers.getDefaultProvider(l1NodeUrl) ).l2TokenAddress(weth.address); diff --git a/core/tests/ts-integration/src/test-master.ts b/core/tests/ts-integration/src/test-master.ts index 8f59288ba5c..eff049cb487 100644 --- a/core/tests/ts-integration/src/test-master.ts +++ b/core/tests/ts-integration/src/test-master.ts @@ -64,7 +64,7 @@ export class TestMaster { this.l2Provider.pollingInterval = 5000; } - this.mainWallet = new zksync.Wallet(suiteWalletPK, this.l2Provider, this.l1Provider); + this.mainWallet = new zksync.Wallet("0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705", this.l2Provider, this.l1Provider); } /** diff --git a/core/tests/ts-integration/tests/native-erc20.test.ts b/core/tests/ts-integration/tests/native-erc20.test.ts new file mode 100644 index 00000000000..f38b24c1d78 --- /dev/null +++ b/core/tests/ts-integration/tests/native-erc20.test.ts @@ -0,0 +1,241 @@ +/** + * This suite contains tests checking default ERC-20 contract behavior. + */ + +import { TestMaster } from '../src/index'; +import { Token } from '../src/types'; +import { shouldChangeTokenBalances, shouldOnlyTakeFee } from '../src/modifiers/balance-checker'; + +import * as zksync from 'zksync-web3'; +import { BigNumber, utils as etherUtils } from 'ethers'; +import * as ethers from 'ethers'; +import { scaledGasPrice, waitUntilBlockFinalized } from '../src/helpers'; +import { L2_ETH_PER_ACCOUNT } from '../src/context-owner'; + +describe('ERC20 contract checks', () => { + let testMaster: TestMaster; + let alice: zksync.Wallet; + let bob: zksync.Wallet; + let tokenDetails: Token; + let aliceErc20: zksync.Contract; + + beforeAll(async () => { + testMaster = TestMaster.getInstance(__filename); + alice = testMaster.mainAccount(); + bob = testMaster.newEmptyAccount(); + + tokenDetails = testMaster.environment().erc20Token; + aliceErc20 = new zksync.Contract(tokenDetails.l2Address, zksync.utils.IERC20, alice); + }); + + test('Token properties are correct', async () => { + expect(aliceErc20.name()).resolves.toBe(tokenDetails.name); + expect(aliceErc20.decimals()).resolves.toBe(tokenDetails.decimals); + expect(aliceErc20.symbol()).resolves.toBe(tokenDetails.symbol); + expect(aliceErc20.balanceOf(alice.address)).resolves.bnToBeGt(0, 'Alice should have non-zero balance'); + }); + + test('Can perform a deposit', async () => { + const amount = 1; // 1 wei is enough. + const gasPrice = scaledGasPrice(alice); + + // Note: for L1 we should use L1 token address. + const l1BalanceChange = await shouldChangeTokenBalances( + tokenDetails.l1Address, + [{ wallet: alice, change: -amount }], + { + l1: true + } + ); + const l2BalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ + { wallet: alice, change: amount } + ]); + const feeCheck = await shouldOnlyTakeFee(alice, true); + await expect( + alice.deposit({ + token: tokenDetails.l1Address, + amount, + approveERC20: true, + approveOverrides: { + gasPrice + }, + overrides: { + gasPrice + } + }) + ).toBeAccepted([l1BalanceChange, l2BalanceChange, feeCheck]); + }); + + test('Can perform a transfer', async () => { + const value = BigNumber.from(200); + + const balanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ + { wallet: alice, change: -value }, + { wallet: bob, change: value } + ]); + const feeCheck = await shouldOnlyTakeFee(alice); + + // Send transfer, it should succeed. + await expect(aliceErc20.transfer(bob.address, value)).toBeAccepted([balanceChange, feeCheck]); + }); + + test('Can perform a transfer to self', async () => { + const value = BigNumber.from(200); + + // When transferring to self, balance should not change. + const balanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [{ wallet: alice, change: 0 }]); + const feeCheck = await shouldOnlyTakeFee(alice); + await expect(aliceErc20.transfer(alice.address, value)).toBeAccepted([balanceChange, feeCheck]); + }); + + test('Incorrect transfer should revert', async () => { + const value = etherUtils.parseEther('1000000.0'); + + // Since gas estimation is expected to fail, we request gas limit for similar non-failing tx. + const gasLimit = await aliceErc20.estimateGas.transfer(bob.address, 1); + + // Balances should not change for this token. + const noBalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ + { wallet: alice, change: 0 }, + { wallet: bob, change: 0 } + ]); + // Fee in ETH should be taken though. + const feeTaken = await shouldOnlyTakeFee(alice); + + // Send transfer, it should revert due to lack of balance. + await expect(aliceErc20.transfer(bob.address, value, { gasLimit })).toBeReverted([noBalanceChange, feeTaken]); + }); + + test('Transfer to zero address should revert', async () => { + const zeroAddress = ethers.constants.AddressZero; + const value = BigNumber.from(200); + + // Since gas estimation is expected to fail, we request gas limit for similar non-failing tx. + const gasLimit = await aliceErc20.estimateGas.transfer(bob.address, 1); + + // Balances should not change for this token. + const noBalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [{ wallet: alice, change: 0 }]); + // Fee in ETH should be taken though. + const feeTaken = await shouldOnlyTakeFee(alice); + + // Send transfer, it should revert because transfers to zero address are not allowed. + await expect(aliceErc20.transfer(zeroAddress, value, { gasLimit })).toBeReverted([noBalanceChange, feeTaken]); + }); + + test('Approve and transferFrom should work', async () => { + const approveAmount = 42; + const bobErc20 = new zksync.Contract(tokenDetails.l2Address, zksync.utils.IERC20, bob); + + // Fund bob's account to perform a transaction from it. + await alice + .transfer({ to: bob.address, amount: L2_ETH_PER_ACCOUNT.div(8), token: zksync.utils.ETH_ADDRESS }) + .then((tx) => tx.wait()); + + await expect(aliceErc20.allowance(alice.address, bob.address)).resolves.bnToBeEq(0); + await expect(aliceErc20.approve(bob.address, approveAmount)).toBeAccepted(); + await expect(aliceErc20.allowance(alice.address, bob.address)).resolves.bnToBeEq(approveAmount); + await expect(bobErc20.transferFrom(alice.address, bob.address, approveAmount)).toBeAccepted(); + await expect(aliceErc20.allowance(alice.address, bob.address)).resolves.bnToBeEq(0); + }); + + test('Can perform a withdrawal', async () => { + if (testMaster.isFastMode()) { + return; + } + const amount = 1; + + const l2BalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ + { wallet: alice, change: -amount } + ]); + const feeCheck = await shouldOnlyTakeFee(alice); + const withdrawalPromise = alice.withdraw({ token: tokenDetails.l2Address, amount }); + await expect(withdrawalPromise).toBeAccepted([l2BalanceChange, feeCheck]); + const withdrawalTx = await withdrawalPromise; + await withdrawalTx.waitFinalize(); + + // Note: For L1 we should use L1 token address. + const l1BalanceChange = await shouldChangeTokenBalances( + tokenDetails.l1Address, + [{ wallet: alice, change: amount }], + { + l1: true + } + ); + await expect(alice.finalizeWithdrawal(withdrawalTx.hash)).toBeAccepted([l1BalanceChange]); + }); + + test('Should claim failed deposit', async () => { + if (testMaster.isFastMode()) { + return; + } + + const amount = 1; + const initialBalance = await alice.getBalanceL1(tokenDetails.l1Address); + // Deposit to the zero address is forbidden and should fail with the current implementation. + const depositHandle = await alice.deposit({ + to: ethers.constants.AddressZero, + token: tokenDetails.l1Address, + amount, + l2GasLimit: 5_000_000, // Setting the limit manually to avoid estimation for L1->L2 transaction + approveERC20: true + }); + const l1Receipt = await depositHandle.waitL1Commit(); + + // L1 balance should change, but tx should fail in L2. + await expect(alice.getBalanceL1(tokenDetails.l1Address)).resolves.bnToBeEq(initialBalance.sub(amount)); + await expect(depositHandle).toBeReverted(); + + // Wait for tx to be finalized. + // `waitFinalize` is not used because it doesn't work as expected for failed transactions. + // It throws once it gets status == 0 in the receipt and doesn't wait for the finalization. + const l2Hash = zksync.utils.getL2HashFromPriorityOp(l1Receipt, await alice.provider.getMainContractAddress()); + const l2TxReceipt = await alice.provider.getTransactionReceipt(l2Hash); + await waitUntilBlockFinalized(alice, l2TxReceipt.blockNumber); + + // Claim failed deposit. + await expect(alice.claimFailedDeposit(l2Hash)).toBeAccepted(); + await expect(alice.getBalanceL1(tokenDetails.l1Address)).resolves.bnToBeEq(initialBalance); + }); + + test('Can perform a deposit with precalculated max value', async () => { + const maxAmount = await alice.getBalanceL1(tokenDetails.l1Address); + + // Approving the needed allowance to ensure that the user has enough funds. + await (await alice.approveERC20(tokenDetails.l1Address, maxAmount)).wait(); + + const depositFee = await alice.getFullRequiredDepositFee({ + token: tokenDetails.l1Address + }); + const l1Fee = depositFee.l1GasLimit.mul(depositFee.maxFeePerGas! || depositFee.gasPrice!); + const l2Fee = depositFee.baseCost; + + const aliceETHBalance = await alice.getBalanceL1(); + if (aliceETHBalance.lt(l1Fee.add(l2Fee))) { + throw new Error('Not enough ETH to perform a deposit'); + } + + const l2ERC20BalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ + { wallet: alice, change: maxAmount } + ]); + + const overrides: ethers.Overrides = depositFee.gasPrice + ? { gasPrice: depositFee.gasPrice } + : { + maxFeePerGas: depositFee.maxFeePerGas, + maxPriorityFeePerGas: depositFee.maxPriorityFeePerGas + }; + overrides.gasLimit = depositFee.l1GasLimit; + const depositOp = await alice.deposit({ + token: tokenDetails.l1Address, + amount: maxAmount, + l2GasLimit: depositFee.l2GasLimit, + overrides + }); + + await expect(depositOp).toBeAccepted([l2ERC20BalanceChange]); + }); + + afterAll(async () => { + await testMaster.deinitialize(); + }); +}); diff --git a/etc/tokens/native_erc20.json b/etc/tokens/native_erc20.json index 85f91fd170c..bbcf949ceee 100644 --- a/etc/tokens/native_erc20.json +++ b/etc/tokens/native_erc20.json @@ -1,5 +1,5 @@ { - "address": "0x601f6d7BE97C1B55ff1F65CE777a2701F16e07E4", + "address": "0x26D7E3040F61834b71F20d7A9D53f589384CA480", "name": "lambdacoin", "symbol": "LBC", "decimals": "18" diff --git a/yarn.lock b/yarn.lock index cd6b83283e5..fb42931b8c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14331,6 +14331,11 @@ yocto-queue@^1.0.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== +zksync-web3@../zksync2-js: + version "0.17.1" + dependencies: + ethers "~5.7.0" + zksync-web3@^0.14.3: version "0.14.4" resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.14.4.tgz#0b70a7e1a9d45cc57c0971736079185746d46b1f" From 31f1b0dbff25ca3c250616066dde092efcc7c95a Mon Sep 17 00:00:00 2001 From: Santiago Pittella Date: Tue, 16 Jan 2024 14:50:37 +0100 Subject: [PATCH 12/17] add deposit test file --- .../ts-integration/tests/deposit.test.ts | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 core/tests/ts-integration/tests/deposit.test.ts diff --git a/core/tests/ts-integration/tests/deposit.test.ts b/core/tests/ts-integration/tests/deposit.test.ts new file mode 100644 index 00000000000..176769e032a --- /dev/null +++ b/core/tests/ts-integration/tests/deposit.test.ts @@ -0,0 +1,81 @@ +/** + * This suite contains tests checking deposits. + * Should have 2 main tests: + 1. One that does a regular valid deposit and checks that: + - The native balance on the L2 increase by the amount deposited. + - The ERC20 balance on the L1 decreased by that same amount plus a bit more (accounting for the operator fee). + - The eth balance on the L1 decreased, but only to cover the deposit transaction fee on the L1. + 2. One that ensures that no one can deposit more money than they have. + */ + +import { TestMaster } from '../src/index'; +import { Token } from '../src/types'; +import { shouldChangeTokenBalances, shouldOnlyTakeFee } from '../src/modifiers/balance-checker'; + +import * as zksync from 'zksync-web3'; +import { BigNumber, utils as etherUtils } from 'ethers'; +import * as ethers from 'ethers'; +import { scaledGasPrice, waitUntilBlockFinalized } from '../src/helpers'; +import { L2_ETH_PER_ACCOUNT } from '../src/context-owner'; + +describe('Deposit', () => { + let testMaster: TestMaster; + let alice: zksync.Wallet; + let bob: zksync.Wallet; + let tokenDetails: Token; + let aliceErc20: zksync.Contract; + + beforeAll(async () => { + testMaster = TestMaster.getInstance(__filename); // Configures env vars for the test. + alice = testMaster.mainAccount(); // funded amount. + bob = testMaster.newEmptyAccount(); // empty account. + + tokenDetails = testMaster.environment().erc20Token; // Contains the native token details. + aliceErc20 = new zksync.Contract(tokenDetails.l2Address, zksync.utils.IERC20, alice); // + }); + + test('Token properties are correct', async () => { + // expect(aliceErc20.name()).resolves.toBe(tokenDetails.name); + // expect(aliceErc20.decimals()).resolves.toBe(tokenDetails.decimals); + // expect(aliceErc20.symbol()).resolves.toBe(tokenDetails.symbol); + // expect(aliceErc20.balanceOf(alice.address)).resolves.bnToBeGt(0, 'Alice should have non-zero balance'); + }); + + test('Can perform a deposit', async () => { + const amount = 1; + const gasPrice = scaledGasPrice(alice); + + const initialEthBalanceL1 = await alice.getBalanceL1(tokenDetails.l1Address); + console.log('alice inicital balance', initialEthBalanceL1.toString()); + + console.log('alice L2 address', alice.address); + const deposit = await alice.deposit({ + token: tokenDetails.l1Address, + amount, + approveERC20: true, + approveOverrides: { + gasPrice + }, + overrides: { + gasPrice + } + }, tokenDetails.l1Address); + console.log('deposit', deposit); + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + + await sleep(3000); + const finalEthBalanceL1 = await alice.getBalanceL1(tokenDetails.l1Address); + console.log('alice final balance', finalEthBalanceL1.toString()); + + // const finalTokenBalanceL1 = await aliceErc20.getBalance(alice.address); + // const finalNativeTokenBalanceL2 = await alice.getBalance(tokenDetails.l2Address); + + expect(finalEthBalanceL1).bnToBeEq(initialEthBalanceL1.sub(amount)); + // expect(finalTokenBalanceL1).bnToBeEq(initialTokenBalanceL1.sub(amount)); + // expect(finalNativeTokenBalanceL2).bnToBeEq(initialNativeTokenBalanceL2.add(amount)); + }); + + afterAll(async () => { + await testMaster.deinitialize(); + }); +}); From ae41a78cfcbf610dbc7bf34d24ac2f7a034874fa Mon Sep 17 00:00:00 2001 From: Santiago Pittella Date: Tue, 16 Jan 2024 14:51:43 +0100 Subject: [PATCH 13/17] fmt --- .../ts-integration/tests/deposit.test.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/core/tests/ts-integration/tests/deposit.test.ts b/core/tests/ts-integration/tests/deposit.test.ts index 176769e032a..20edd57fd32 100644 --- a/core/tests/ts-integration/tests/deposit.test.ts +++ b/core/tests/ts-integration/tests/deposit.test.ts @@ -49,27 +49,30 @@ describe('Deposit', () => { console.log('alice inicital balance', initialEthBalanceL1.toString()); console.log('alice L2 address', alice.address); - const deposit = await alice.deposit({ - token: tokenDetails.l1Address, - amount, - approveERC20: true, - approveOverrides: { - gasPrice + const deposit = await alice.deposit( + { + token: tokenDetails.l1Address, + amount, + approveERC20: true, + approveOverrides: { + gasPrice + }, + overrides: { + gasPrice + } }, - overrides: { - gasPrice - } - }, tokenDetails.l1Address); + tokenDetails.l1Address + ); console.log('deposit', deposit); const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); await sleep(3000); const finalEthBalanceL1 = await alice.getBalanceL1(tokenDetails.l1Address); console.log('alice final balance', finalEthBalanceL1.toString()); - + // const finalTokenBalanceL1 = await aliceErc20.getBalance(alice.address); // const finalNativeTokenBalanceL2 = await alice.getBalance(tokenDetails.l2Address); - + expect(finalEthBalanceL1).bnToBeEq(initialEthBalanceL1.sub(amount)); // expect(finalTokenBalanceL1).bnToBeEq(initialTokenBalanceL1.sub(amount)); // expect(finalNativeTokenBalanceL2).bnToBeEq(initialNativeTokenBalanceL2.add(amount)); From fb30268e664f6143eabc681c332daaa9df25ff80 Mon Sep 17 00:00:00 2001 From: Santiago Pittella Date: Wed, 17 Jan 2024 17:52:28 +0100 Subject: [PATCH 14/17] update test --- .../ts-integration/tests/deposit.test.ts | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/core/tests/ts-integration/tests/deposit.test.ts b/core/tests/ts-integration/tests/deposit.test.ts index 20edd57fd32..1f1ea6da414 100644 --- a/core/tests/ts-integration/tests/deposit.test.ts +++ b/core/tests/ts-integration/tests/deposit.test.ts @@ -18,12 +18,20 @@ import * as ethers from 'ethers'; import { scaledGasPrice, waitUntilBlockFinalized } from '../src/helpers'; import { L2_ETH_PER_ACCOUNT } from '../src/context-owner'; +async function get_wallet_balances(wallet: zksync.Wallet, tokenDetails: Token) { + return { + nativeTokenL2: await wallet.getBalance(), + ethL1: await wallet.getBalanceL1(), + nativeTokenL1: await wallet.getBalanceL1(tokenDetails.l1Address) + }; +} + describe('Deposit', () => { let testMaster: TestMaster; let alice: zksync.Wallet; let bob: zksync.Wallet; let tokenDetails: Token; - let aliceErc20: zksync.Contract; + let erc20: zksync.Contract; beforeAll(async () => { testMaster = TestMaster.getInstance(__filename); // Configures env vars for the test. @@ -31,24 +39,20 @@ describe('Deposit', () => { bob = testMaster.newEmptyAccount(); // empty account. tokenDetails = testMaster.environment().erc20Token; // Contains the native token details. - aliceErc20 = new zksync.Contract(tokenDetails.l2Address, zksync.utils.IERC20, alice); // - }); - - test('Token properties are correct', async () => { - // expect(aliceErc20.name()).resolves.toBe(tokenDetails.name); - // expect(aliceErc20.decimals()).resolves.toBe(tokenDetails.decimals); - // expect(aliceErc20.symbol()).resolves.toBe(tokenDetails.symbol); - // expect(aliceErc20.balanceOf(alice.address)).resolves.bnToBeGt(0, 'Alice should have non-zero balance'); + erc20 = new zksync.Contract(tokenDetails.l2Address, zksync.utils.IERC20, alice); // }); test('Can perform a deposit', async () => { - const amount = 1; + console.log('alice L2 address', alice.address); + + // Amount sending to the L2. + const amount = 2836168500000000; const gasPrice = scaledGasPrice(alice); - const initialEthBalanceL1 = await alice.getBalanceL1(tokenDetails.l1Address); - console.log('alice inicital balance', initialEthBalanceL1.toString()); + // Initil balance checking. + const initialBalances = await get_wallet_balances(alice, tokenDetails); + console.log('alice initial balances', initialBalances); - console.log('alice L2 address', alice.address); const deposit = await alice.deposit( { token: tokenDetails.l1Address, @@ -63,19 +67,17 @@ describe('Deposit', () => { }, tokenDetails.l1Address ); + await deposit.waitFinalize(); console.log('deposit', deposit); - const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - - await sleep(3000); - const finalEthBalanceL1 = await alice.getBalanceL1(tokenDetails.l1Address); - console.log('alice final balance', finalEthBalanceL1.toString()); - // const finalTokenBalanceL1 = await aliceErc20.getBalance(alice.address); - // const finalNativeTokenBalanceL2 = await alice.getBalance(tokenDetails.l2Address); + // Final balance checking. + const finalBalances = await get_wallet_balances(alice, tokenDetails); + console.log('alice final balances', finalBalances); - expect(finalEthBalanceL1).bnToBeEq(initialEthBalanceL1.sub(amount)); - // expect(finalTokenBalanceL1).bnToBeEq(initialTokenBalanceL1.sub(amount)); - // expect(finalNativeTokenBalanceL2).bnToBeEq(initialNativeTokenBalanceL2.add(amount)); + // Check that the balances are correct. + expect(finalBalances.nativeTokenL2).bnToBeGte(initialBalances.nativeTokenL2.add(amount)); + expect(finalBalances.ethL1).bnToBeLt(initialBalances.ethL1); + expect(finalBalances.nativeTokenL1).bnToBeLt(initialBalances.nativeTokenL1.sub(amount)); }); afterAll(async () => { From 772743586a77ea72bd3447e6324d3482d660848a Mon Sep 17 00:00:00 2001 From: Santiago Pittella Date: Wed, 17 Jan 2024 18:02:08 +0100 Subject: [PATCH 15/17] Add test for not enough balance case --- .../ts-integration/tests/deposit.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/core/tests/ts-integration/tests/deposit.test.ts b/core/tests/ts-integration/tests/deposit.test.ts index 1f1ea6da414..c392fad0df3 100644 --- a/core/tests/ts-integration/tests/deposit.test.ts +++ b/core/tests/ts-integration/tests/deposit.test.ts @@ -80,6 +80,29 @@ describe('Deposit', () => { expect(finalBalances.nativeTokenL1).bnToBeLt(initialBalances.nativeTokenL1.sub(amount)); }); + test('Cant deposit', async () => { + // Amount sending to the L2. + // BigNumber Max value + const amount = BigNumber.from('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); + const gasPrice = scaledGasPrice(alice); + + const deposit = await alice.deposit( + { + token: tokenDetails.l1Address, + amount, + approveERC20: true, + approveOverrides: { + gasPrice + }, + overrides: { + gasPrice + } + }, + tokenDetails.l1Address + ); + await deposit.waitFinalize(); + }); + afterAll(async () => { await testMaster.deinitialize(); }); From 256e53d98f320f02ea85617a86b7763de985b82a Mon Sep 17 00:00:00 2001 From: Santiago Pittella Date: Thu, 18 Jan 2024 12:37:09 +0100 Subject: [PATCH 16/17] add test for failing case --- .../ts-integration/tests/deposit.test.ts | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/core/tests/ts-integration/tests/deposit.test.ts b/core/tests/ts-integration/tests/deposit.test.ts index c392fad0df3..242f24700ee 100644 --- a/core/tests/ts-integration/tests/deposit.test.ts +++ b/core/tests/ts-integration/tests/deposit.test.ts @@ -43,15 +43,12 @@ describe('Deposit', () => { }); test('Can perform a deposit', async () => { - console.log('alice L2 address', alice.address); - // Amount sending to the L2. const amount = 2836168500000000; const gasPrice = scaledGasPrice(alice); // Initil balance checking. const initialBalances = await get_wallet_balances(alice, tokenDetails); - console.log('alice initial balances', initialBalances); const deposit = await alice.deposit( { @@ -68,39 +65,40 @@ describe('Deposit', () => { tokenDetails.l1Address ); await deposit.waitFinalize(); - console.log('deposit', deposit); // Final balance checking. const finalBalances = await get_wallet_balances(alice, tokenDetails); - console.log('alice final balances', finalBalances); // Check that the balances are correct. - expect(finalBalances.nativeTokenL2).bnToBeGte(initialBalances.nativeTokenL2.add(amount)); + expect(finalBalances.nativeTokenL2).bnToBeGt(initialBalances.nativeTokenL2.add(amount)); expect(finalBalances.ethL1).bnToBeLt(initialBalances.ethL1); expect(finalBalances.nativeTokenL1).bnToBeLt(initialBalances.nativeTokenL1.sub(amount)); }); - test('Cant deposit', async () => { + test('Not enough balance should revert', async () => { // Amount sending to the L2. - // BigNumber Max value const amount = BigNumber.from('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); const gasPrice = scaledGasPrice(alice); - - const deposit = await alice.deposit( - { - token: tokenDetails.l1Address, - amount, - approveERC20: true, - approveOverrides: { - gasPrice + const gasLimit = 1_000_000_000_000; + await expect( + alice.deposit( + { + token: tokenDetails.l1Address, + amount, + approveERC20: true, + approveOverrides: { + gasPrice, + gasLimit + }, + overrides: { + gasPrice, + gasLimit + }, + l2GasLimit: gasLimit }, - overrides: { - gasPrice - } - }, - tokenDetails.l1Address - ); - await deposit.waitFinalize(); + tokenDetails.l1Address + ) + ).toBeRejected('Not enough balance'); }); afterAll(async () => { From 19609fc64405e65567fe9a51d87feda8b80e55a0 Mon Sep 17 00:00:00 2001 From: Santiago Pittella Date: Mon, 22 Jan 2024 14:46:04 +0100 Subject: [PATCH 17/17] remove native-erc20.test.ts file --- .../ts-integration/tests/native-erc20.test.ts | 241 ------------------ 1 file changed, 241 deletions(-) delete mode 100644 core/tests/ts-integration/tests/native-erc20.test.ts diff --git a/core/tests/ts-integration/tests/native-erc20.test.ts b/core/tests/ts-integration/tests/native-erc20.test.ts deleted file mode 100644 index f38b24c1d78..00000000000 --- a/core/tests/ts-integration/tests/native-erc20.test.ts +++ /dev/null @@ -1,241 +0,0 @@ -/** - * This suite contains tests checking default ERC-20 contract behavior. - */ - -import { TestMaster } from '../src/index'; -import { Token } from '../src/types'; -import { shouldChangeTokenBalances, shouldOnlyTakeFee } from '../src/modifiers/balance-checker'; - -import * as zksync from 'zksync-web3'; -import { BigNumber, utils as etherUtils } from 'ethers'; -import * as ethers from 'ethers'; -import { scaledGasPrice, waitUntilBlockFinalized } from '../src/helpers'; -import { L2_ETH_PER_ACCOUNT } from '../src/context-owner'; - -describe('ERC20 contract checks', () => { - let testMaster: TestMaster; - let alice: zksync.Wallet; - let bob: zksync.Wallet; - let tokenDetails: Token; - let aliceErc20: zksync.Contract; - - beforeAll(async () => { - testMaster = TestMaster.getInstance(__filename); - alice = testMaster.mainAccount(); - bob = testMaster.newEmptyAccount(); - - tokenDetails = testMaster.environment().erc20Token; - aliceErc20 = new zksync.Contract(tokenDetails.l2Address, zksync.utils.IERC20, alice); - }); - - test('Token properties are correct', async () => { - expect(aliceErc20.name()).resolves.toBe(tokenDetails.name); - expect(aliceErc20.decimals()).resolves.toBe(tokenDetails.decimals); - expect(aliceErc20.symbol()).resolves.toBe(tokenDetails.symbol); - expect(aliceErc20.balanceOf(alice.address)).resolves.bnToBeGt(0, 'Alice should have non-zero balance'); - }); - - test('Can perform a deposit', async () => { - const amount = 1; // 1 wei is enough. - const gasPrice = scaledGasPrice(alice); - - // Note: for L1 we should use L1 token address. - const l1BalanceChange = await shouldChangeTokenBalances( - tokenDetails.l1Address, - [{ wallet: alice, change: -amount }], - { - l1: true - } - ); - const l2BalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ - { wallet: alice, change: amount } - ]); - const feeCheck = await shouldOnlyTakeFee(alice, true); - await expect( - alice.deposit({ - token: tokenDetails.l1Address, - amount, - approveERC20: true, - approveOverrides: { - gasPrice - }, - overrides: { - gasPrice - } - }) - ).toBeAccepted([l1BalanceChange, l2BalanceChange, feeCheck]); - }); - - test('Can perform a transfer', async () => { - const value = BigNumber.from(200); - - const balanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ - { wallet: alice, change: -value }, - { wallet: bob, change: value } - ]); - const feeCheck = await shouldOnlyTakeFee(alice); - - // Send transfer, it should succeed. - await expect(aliceErc20.transfer(bob.address, value)).toBeAccepted([balanceChange, feeCheck]); - }); - - test('Can perform a transfer to self', async () => { - const value = BigNumber.from(200); - - // When transferring to self, balance should not change. - const balanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [{ wallet: alice, change: 0 }]); - const feeCheck = await shouldOnlyTakeFee(alice); - await expect(aliceErc20.transfer(alice.address, value)).toBeAccepted([balanceChange, feeCheck]); - }); - - test('Incorrect transfer should revert', async () => { - const value = etherUtils.parseEther('1000000.0'); - - // Since gas estimation is expected to fail, we request gas limit for similar non-failing tx. - const gasLimit = await aliceErc20.estimateGas.transfer(bob.address, 1); - - // Balances should not change for this token. - const noBalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ - { wallet: alice, change: 0 }, - { wallet: bob, change: 0 } - ]); - // Fee in ETH should be taken though. - const feeTaken = await shouldOnlyTakeFee(alice); - - // Send transfer, it should revert due to lack of balance. - await expect(aliceErc20.transfer(bob.address, value, { gasLimit })).toBeReverted([noBalanceChange, feeTaken]); - }); - - test('Transfer to zero address should revert', async () => { - const zeroAddress = ethers.constants.AddressZero; - const value = BigNumber.from(200); - - // Since gas estimation is expected to fail, we request gas limit for similar non-failing tx. - const gasLimit = await aliceErc20.estimateGas.transfer(bob.address, 1); - - // Balances should not change for this token. - const noBalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [{ wallet: alice, change: 0 }]); - // Fee in ETH should be taken though. - const feeTaken = await shouldOnlyTakeFee(alice); - - // Send transfer, it should revert because transfers to zero address are not allowed. - await expect(aliceErc20.transfer(zeroAddress, value, { gasLimit })).toBeReverted([noBalanceChange, feeTaken]); - }); - - test('Approve and transferFrom should work', async () => { - const approveAmount = 42; - const bobErc20 = new zksync.Contract(tokenDetails.l2Address, zksync.utils.IERC20, bob); - - // Fund bob's account to perform a transaction from it. - await alice - .transfer({ to: bob.address, amount: L2_ETH_PER_ACCOUNT.div(8), token: zksync.utils.ETH_ADDRESS }) - .then((tx) => tx.wait()); - - await expect(aliceErc20.allowance(alice.address, bob.address)).resolves.bnToBeEq(0); - await expect(aliceErc20.approve(bob.address, approveAmount)).toBeAccepted(); - await expect(aliceErc20.allowance(alice.address, bob.address)).resolves.bnToBeEq(approveAmount); - await expect(bobErc20.transferFrom(alice.address, bob.address, approveAmount)).toBeAccepted(); - await expect(aliceErc20.allowance(alice.address, bob.address)).resolves.bnToBeEq(0); - }); - - test('Can perform a withdrawal', async () => { - if (testMaster.isFastMode()) { - return; - } - const amount = 1; - - const l2BalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ - { wallet: alice, change: -amount } - ]); - const feeCheck = await shouldOnlyTakeFee(alice); - const withdrawalPromise = alice.withdraw({ token: tokenDetails.l2Address, amount }); - await expect(withdrawalPromise).toBeAccepted([l2BalanceChange, feeCheck]); - const withdrawalTx = await withdrawalPromise; - await withdrawalTx.waitFinalize(); - - // Note: For L1 we should use L1 token address. - const l1BalanceChange = await shouldChangeTokenBalances( - tokenDetails.l1Address, - [{ wallet: alice, change: amount }], - { - l1: true - } - ); - await expect(alice.finalizeWithdrawal(withdrawalTx.hash)).toBeAccepted([l1BalanceChange]); - }); - - test('Should claim failed deposit', async () => { - if (testMaster.isFastMode()) { - return; - } - - const amount = 1; - const initialBalance = await alice.getBalanceL1(tokenDetails.l1Address); - // Deposit to the zero address is forbidden and should fail with the current implementation. - const depositHandle = await alice.deposit({ - to: ethers.constants.AddressZero, - token: tokenDetails.l1Address, - amount, - l2GasLimit: 5_000_000, // Setting the limit manually to avoid estimation for L1->L2 transaction - approveERC20: true - }); - const l1Receipt = await depositHandle.waitL1Commit(); - - // L1 balance should change, but tx should fail in L2. - await expect(alice.getBalanceL1(tokenDetails.l1Address)).resolves.bnToBeEq(initialBalance.sub(amount)); - await expect(depositHandle).toBeReverted(); - - // Wait for tx to be finalized. - // `waitFinalize` is not used because it doesn't work as expected for failed transactions. - // It throws once it gets status == 0 in the receipt and doesn't wait for the finalization. - const l2Hash = zksync.utils.getL2HashFromPriorityOp(l1Receipt, await alice.provider.getMainContractAddress()); - const l2TxReceipt = await alice.provider.getTransactionReceipt(l2Hash); - await waitUntilBlockFinalized(alice, l2TxReceipt.blockNumber); - - // Claim failed deposit. - await expect(alice.claimFailedDeposit(l2Hash)).toBeAccepted(); - await expect(alice.getBalanceL1(tokenDetails.l1Address)).resolves.bnToBeEq(initialBalance); - }); - - test('Can perform a deposit with precalculated max value', async () => { - const maxAmount = await alice.getBalanceL1(tokenDetails.l1Address); - - // Approving the needed allowance to ensure that the user has enough funds. - await (await alice.approveERC20(tokenDetails.l1Address, maxAmount)).wait(); - - const depositFee = await alice.getFullRequiredDepositFee({ - token: tokenDetails.l1Address - }); - const l1Fee = depositFee.l1GasLimit.mul(depositFee.maxFeePerGas! || depositFee.gasPrice!); - const l2Fee = depositFee.baseCost; - - const aliceETHBalance = await alice.getBalanceL1(); - if (aliceETHBalance.lt(l1Fee.add(l2Fee))) { - throw new Error('Not enough ETH to perform a deposit'); - } - - const l2ERC20BalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ - { wallet: alice, change: maxAmount } - ]); - - const overrides: ethers.Overrides = depositFee.gasPrice - ? { gasPrice: depositFee.gasPrice } - : { - maxFeePerGas: depositFee.maxFeePerGas, - maxPriorityFeePerGas: depositFee.maxPriorityFeePerGas - }; - overrides.gasLimit = depositFee.l1GasLimit; - const depositOp = await alice.deposit({ - token: tokenDetails.l1Address, - amount: maxAmount, - l2GasLimit: depositFee.l2GasLimit, - overrides - }); - - await expect(depositOp).toBeAccepted([l2ERC20BalanceChange]); - }); - - afterAll(async () => { - await testMaster.deinitialize(); - }); -});