diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index 3cd654dddfd..10365584bb9 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -5,6 +5,7 @@ use clap::Parser; use zksync_config::{ configs::{ api::{HealthCheckConfig, MerkleTreeApiConfig, Web3JsonRpcConfig}, + base_token_fetcher::BaseTokenFetcherConfig, chain::{ CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, StateKeeperConfig, @@ -273,5 +274,6 @@ fn load_env_config() -> anyhow::Result { object_store_config: ObjectStoreConfig::from_env().ok(), observability: ObservabilityConfig::from_env().ok(), snapshot_creator: SnapshotsCreatorConfig::from_env().ok(), + base_token_fetcher: BaseTokenFetcherConfig::from_env().ok(), }) } diff --git a/core/lib/config/src/configs/base_token_fetcher.rs b/core/lib/config/src/configs/base_token_fetcher.rs new file mode 100644 index 00000000000..5a74aec3438 --- /dev/null +++ b/core/lib/config/src/configs/base_token_fetcher.rs @@ -0,0 +1,9 @@ +use serde::Deserialize; +use zksync_basic_types::Address; + +#[derive(Debug, Clone, Deserialize, PartialEq)] +pub struct BaseTokenFetcherConfig { + pub poll_interval: u64, + pub host: String, + pub token_address: Address, +} diff --git a/core/lib/config/src/configs/general.rs b/core/lib/config/src/configs/general.rs index e3097bdf5f5..cf150e01adc 100644 --- a/core/lib/config/src/configs/general.rs +++ b/core/lib/config/src/configs/general.rs @@ -1,3 +1,4 @@ +use super::BaseTokenFetcherConfig; use crate::{ configs::{ chain::{CircuitBreakerConfig, MempoolConfig, OperationsManagerConfig, StateKeeperConfig}, @@ -32,4 +33,5 @@ pub struct GeneralConfig { pub eth: Option, pub snapshot_creator: Option, pub observability: Option, + pub base_token_fetcher: Option, } diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index 19905e5d757..019ec7b8691 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -1,6 +1,7 @@ // Public re-exports pub use self::{ api::ApiConfig, + base_token_fetcher::BaseTokenFetcherConfig, contract_verifier::ContractVerifierConfig, contracts::ContractsConfig, database::{DBConfig, PostgresConfig}, @@ -22,6 +23,7 @@ pub use self::{ }; pub mod api; +pub mod base_token_fetcher; pub mod chain; pub mod contract_verifier; pub mod contracts; diff --git a/core/lib/dal/src/blocks_dal.rs b/core/lib/dal/src/blocks_dal.rs index d930784612c..fe2b5d451b5 100644 --- a/core/lib/dal/src/blocks_dal.rs +++ b/core/lib/dal/src/blocks_dal.rs @@ -2309,7 +2309,7 @@ impl BlocksDal<'_, '_> { ) WHERE l1_batch_number IS NULL - AND fee_account_address = '\x0000000000000000000000000000000000000000'::bytea + AND fee_account_address = 'x0000000000000000000000000000000000000000'::bytea "# ) .execute(self.storage.conn()) @@ -2349,7 +2349,7 @@ impl BlocksDal<'_, '_> { WHERE l1_batches.number = miniblocks.l1_batch_number AND miniblocks.number BETWEEN $1 AND $2 - AND miniblocks.fee_account_address = '\x0000000000000000000000000000000000000000'::bytea + AND miniblocks.fee_account_address = 'x0000000000000000000000000000000000000000'::bytea "#, i64::from(numbers.start().0), i64::from(numbers.end().0) diff --git a/core/lib/env_config/src/base_token_fetcher.rs b/core/lib/env_config/src/base_token_fetcher.rs new file mode 100644 index 00000000000..78d559bda90 --- /dev/null +++ b/core/lib/env_config/src/base_token_fetcher.rs @@ -0,0 +1,9 @@ +use zksync_config::configs::base_token_fetcher::BaseTokenFetcherConfig; + +use crate::{envy_load, FromEnv}; + +impl FromEnv for BaseTokenFetcherConfig { + fn from_env() -> anyhow::Result { + envy_load("base_token_fetcher", "BASE_TOKEN_FETCHER_") + } +} diff --git a/core/lib/env_config/src/lib.rs b/core/lib/env_config/src/lib.rs index a937cc89a3f..6607a12efae 100644 --- a/core/lib/env_config/src/lib.rs +++ b/core/lib/env_config/src/lib.rs @@ -2,6 +2,7 @@ use anyhow::Context as _; use serde::de::DeserializeOwned; mod api; +pub mod base_token_fetcher; mod chain; mod contract_verifier; mod contracts; diff --git a/core/lib/protobuf_config/src/base_token_fetcher.rs b/core/lib/protobuf_config/src/base_token_fetcher.rs new file mode 100644 index 00000000000..7f54cb797bb --- /dev/null +++ b/core/lib/protobuf_config/src/base_token_fetcher.rs @@ -0,0 +1,26 @@ +use anyhow::{Context as _, Ok}; +use zksync_config::configs; +use zksync_protobuf::{repr::ProtoRepr, required}; + +use crate::{parse_h160, proto::base_token_fetcher as proto}; + +impl ProtoRepr for proto::BaseTokenFetcher { + type Type = configs::BaseTokenFetcherConfig; + fn read(&self) -> anyhow::Result { + Ok(Self::Type { + poll_interval: *required(&self.poll_interval).context("poll_interval")?, + host: required(&self.host).context("host")?.clone(), + token_address: required(&self.token_address) + .and_then(|a| parse_h160(a)) + .context("fee_account_addr")?, + }) + } + + fn build(this: &Self::Type) -> Self { + Self { + poll_interval: Some(this.poll_interval), + host: Some(this.host.clone()), + token_address: Some(this.token_address.to_string()), + } + } +} diff --git a/core/lib/protobuf_config/src/general.rs b/core/lib/protobuf_config/src/general.rs index ccd55a71c2e..161a2418e20 100644 --- a/core/lib/protobuf_config/src/general.rs +++ b/core/lib/protobuf_config/src/general.rs @@ -37,6 +37,8 @@ impl ProtoRepr for proto::GeneralConfig { snapshot_creator: read_optional_repr(&self.snapshot_creator) .context("snapshot_creator")?, observability: read_optional_repr(&self.observability).context("observability")?, + base_token_fetcher: read_optional_repr(&self.base_token_fetcher) + .context("base_token_fetcher")?, }) } @@ -68,6 +70,7 @@ impl ProtoRepr for proto::GeneralConfig { eth: this.eth.as_ref().map(ProtoRepr::build), snapshot_creator: this.snapshot_creator.as_ref().map(ProtoRepr::build), observability: this.observability.as_ref().map(ProtoRepr::build), + base_token_fetcher: this.base_token_fetcher.as_ref().map(ProtoRepr::build), } } } diff --git a/core/lib/protobuf_config/src/lib.rs b/core/lib/protobuf_config/src/lib.rs index 94b9317e961..373ec786d42 100644 --- a/core/lib/protobuf_config/src/lib.rs +++ b/core/lib/protobuf_config/src/lib.rs @@ -5,6 +5,7 @@ //! * protobuf json format mod api; +mod base_token_fetcher; mod chain; mod circuit_breaker; mod contract_verifier; diff --git a/core/lib/protobuf_config/src/proto/base_token_fetcher.proto b/core/lib/protobuf_config/src/proto/base_token_fetcher.proto new file mode 100644 index 00000000000..b366f87b201 --- /dev/null +++ b/core/lib/protobuf_config/src/proto/base_token_fetcher.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package zksync.config.base_token_fetcher; + +message BaseTokenFetcher { + optional uint64 poll_interval = 1; + optional string host = 2; + optional string token_address = 3; +} diff --git a/core/lib/protobuf_config/src/proto/general.proto b/core/lib/protobuf_config/src/proto/general.proto index 179f3c7ed90..9ee0da4f07b 100644 --- a/core/lib/protobuf_config/src/proto/general.proto +++ b/core/lib/protobuf_config/src/proto/general.proto @@ -13,6 +13,8 @@ import "zksync/config/house_keeper.proto"; import "zksync/config/observability.proto"; import "zksync/config/snapshots_creator.proto"; import "zksync/config/utils.proto"; +import "zksync/config/utils.proto"; +import "zksync/config/base_token_fetcher.proto"; message GeneralConfig { optional config.database.Postgres postgres = 1; @@ -35,6 +37,5 @@ message GeneralConfig { optional config.prover.ProverGateway prover_gateway = 30; optional config.snapshot_creator.SnapshotsCreator snapshot_creator = 31; optional config.observability.Observability observability = 32; - + optional config.base_token_fetcher.BaseTokenFetcher base_token_fetcher = 33; } - diff --git a/core/lib/zksync_core/src/base_token_fetcher/mod.rs b/core/lib/zksync_core/src/base_token_fetcher/mod.rs new file mode 100644 index 00000000000..c6c060cd0e1 --- /dev/null +++ b/core/lib/zksync_core/src/base_token_fetcher/mod.rs @@ -0,0 +1,145 @@ +use std::{cmp::min, sync::Arc}; + +use anyhow::Context; +use async_trait::async_trait; +use hex::ToHex; +use metrics::atomics::AtomicU64; +use tokio::sync::Mutex; +use zksync_config::configs::base_token_fetcher::BaseTokenFetcherConfig; + +/// Trait used to query the stack's native token conversion rate. Used to properly +/// determine gas prices, as they partially depend on L1 gas prices, denominated in `eth`. +#[async_trait] +pub trait ConversionRateFetcher: 'static + std::fmt::Debug + Send + Sync { + fn conversion_rate(&self) -> anyhow::Result; + async fn update(&self) -> anyhow::Result<()>; +} + +#[derive(Debug)] +pub(crate) struct NoOpConversionRateFetcher; + +impl NoOpConversionRateFetcher { + pub fn new() -> Self { + Self + } +} + +#[async_trait] +impl ConversionRateFetcher for NoOpConversionRateFetcher { + fn conversion_rate(&self) -> anyhow::Result { + Ok(1) + } + + async fn update(&self) -> anyhow::Result<()> { + Ok(()) + } +} + +/// Struct in charge of periodically querying and caching the native token's conversion rate +/// to `eth`. +#[derive(Debug)] +pub(crate) struct BaseTokenFetcher { + pub config: BaseTokenFetcherConfig, + pub latest_to_eth_conversion_rate: AtomicU64, + http_client: reqwest::Client, + error_reporter: Arc>, +} + +impl BaseTokenFetcher { + pub(crate) async fn new(config: BaseTokenFetcherConfig) -> anyhow::Result { + let http_client = reqwest::Client::new(); + + let conversion_rate = http_client + .get(format!( + "{}/conversion_rate/0x{}", + config.host, + config.token_address.encode_hex::() + )) + .send() + .await? + .json::() + .await + .context("Unable to parse the response of the native token conversion rate server")?; + + let error_reporter = Arc::new(Mutex::new(ErrorReporter::new())); + + Ok(Self { + config, + latest_to_eth_conversion_rate: AtomicU64::new(conversion_rate), + http_client, + error_reporter, + }) + } +} + +#[async_trait] +impl ConversionRateFetcher for BaseTokenFetcher { + fn conversion_rate(&self) -> anyhow::Result { + anyhow::Ok( + self.latest_to_eth_conversion_rate + .load(std::sync::atomic::Ordering::Relaxed), + ) + } + + async fn update(&self) -> anyhow::Result<()> { + match self + .http_client + .get(format!( + "{}/conversion_rate/0x{}", + &self.config.host, + &self.config.token_address.encode_hex::() + )) + .send() + .await + { + Ok(response) => { + let conversion_rate = response.json::().await.context( + "Unable to parse the response of the native token conversion rate server", + )?; + self.latest_to_eth_conversion_rate + .store(conversion_rate, std::sync::atomic::Ordering::Relaxed); + self.error_reporter.lock().await.reset(); + } + Err(err) => self + .error_reporter + .lock() + .await + .process(anyhow::anyhow!(err)), + } + + Ok(()) + } +} + +#[derive(Debug)] +struct ErrorReporter { + current_try: u8, + alert_spawned: bool, +} + +impl ErrorReporter { + const MAX_CONSECUTIVE_NETWORK_ERRORS: u8 = 10; + + fn new() -> Self { + Self { + current_try: 0, + alert_spawned: false, + } + } + + fn reset(&mut self) { + self.current_try = 0; + self.alert_spawned = false; + } + + fn process(&mut self, err: anyhow::Error) { + self.current_try = min(self.current_try + 1, Self::MAX_CONSECUTIVE_NETWORK_ERRORS); + + tracing::error!("Failed to fetch native token conversion rate from the server: {err}"); + + if self.current_try >= Self::MAX_CONSECUTIVE_NETWORK_ERRORS && !self.alert_spawned { + vlog::capture_message(&err.to_string(), vlog::AlertLevel::Warning); + self.alert_spawned = true; + } + } +} diff --git a/core/lib/zksync_core/src/dev_api_conversion_rate/mod.rs b/core/lib/zksync_core/src/dev_api_conversion_rate/mod.rs new file mode 100644 index 00000000000..5f565cb4835 --- /dev/null +++ b/core/lib/zksync_core/src/dev_api_conversion_rate/mod.rs @@ -0,0 +1,35 @@ +use axum::{extract, extract::Json, routing::get, Router}; +use tokio::sync::watch; +use zksync_config::configs::base_token_fetcher::BaseTokenFetcherConfig; + +pub(crate) async fn run_server( + mut stop_receiver: watch::Receiver, + server_configs: &BaseTokenFetcherConfig, +) -> anyhow::Result<()> { + let app = Router::new().route("/conversion_rate/:token_address", get(get_conversion_rate)); + + let bind_address = if server_configs.host.starts_with("http://") { + &server_configs.host[7..] // If it starts with "http://", strip the prefix + } else { + &server_configs.host // Otherwise, return the original string + }; + + axum::Server::bind(&bind_address.parse().expect("Unable to parse socket address")) + .serve(app.into_make_service()) + .with_graceful_shutdown(async move { + if stop_receiver.changed().await.is_err() { + tracing::warn!("Stop signal sender for conversion rate API server was dropped without sending a signal"); + } + tracing::info!("Stop signal received, conversion rate server is shutting down"); + }) + .await + .expect("Conversion rate server failed"); + tracing::info!("Conversion rate server shut down"); + Ok(()) +} + +// basic handler that responds with a static string +async fn get_conversion_rate(extract::Path(_token_address): extract::Path) -> Json { + tracing::info!("Received request for conversion rate"); + Json(42) +} 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 be3462dedf4..2ba279ae8fa 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 @@ -13,7 +13,7 @@ use zksync_types::{U256, U64}; use self::metrics::METRICS; use super::{L1TxParamsProvider, PubdataPricing}; -use crate::state_keeper::metrics::KEEPER_METRICS; +use crate::{base_token_fetcher::ConversionRateFetcher, state_keeper::metrics::KEEPER_METRICS}; mod metrics; #[cfg(test)] @@ -32,6 +32,7 @@ pub struct GasAdjuster { pub(super) config: GasAdjusterConfig, pubdata_sending_mode: PubdataSendingMode, eth_client: Arc, + base_token_fetcher: Arc, pubdata_pricing: Arc, } @@ -40,6 +41,7 @@ impl GasAdjuster { eth_client: Arc, config: GasAdjusterConfig, pubdata_sending_mode: PubdataSendingMode, + base_token_fetcher: Arc, pubdata_pricing: Arc, ) -> Result { // Subtracting 1 from the "latest" block number to prevent errors in case @@ -71,6 +73,7 @@ impl GasAdjuster { &last_block_blob_base_fee, ), config, + base_token_fetcher, pubdata_sending_mode, eth_client, pubdata_pricing, @@ -149,6 +152,13 @@ impl GasAdjuster { tracing::warn!("Cannot add the base fee to gas statistics: {}", err); } + if let Err(err) = self.base_token_fetcher.update().await { + tracing::warn!( + "Error when trying to fetch the native erc20 conversion rate: {}", + err + ); + } + tokio::time::sleep(self.config.poll_period()).await; } Ok(()) @@ -166,8 +176,9 @@ impl GasAdjuster { let calculated_price = (self.config.internal_l1_pricing_multiplier * effective_gas_price as f64) as u64; - // Bound the price if it's too high. - self.bound_gas_price(calculated_price) + let conversion_rate = self.base_token_fetcher.conversion_rate().unwrap_or(1); + + self.bound_gas_price(calculated_price) * conversion_rate } pub(crate) fn estimate_effective_pubdata_price(&self) -> u64 { diff --git a/core/lib/zksync_core/src/l1_gas_price/singleton.rs b/core/lib/zksync_core/src/l1_gas_price/singleton.rs index a24d3a635e2..63669faaa92 100644 --- a/core/lib/zksync_core/src/l1_gas_price/singleton.rs +++ b/core/lib/zksync_core/src/l1_gas_price/singleton.rs @@ -9,7 +9,7 @@ use zksync_config::{configs::eth_sender::PubdataSendingMode, GasAdjusterConfig}; use zksync_eth_client::clients::QueryClient; use super::PubdataPricing; -use crate::l1_gas_price::GasAdjuster; +use crate::{l1_gas_price::GasAdjuster, ConversionRateFetcher}; /// Special struct for creating a singleton of `GasAdjuster`. /// This is needed only for running the server. @@ -19,6 +19,7 @@ pub struct GasAdjusterSingleton { gas_adjuster_config: GasAdjusterConfig, pubdata_sending_mode: PubdataSendingMode, singleton: OnceCell, Error>>, + base_token_fetcher: Arc, pubdata_pricing: Arc, } @@ -37,6 +38,7 @@ impl GasAdjusterSingleton { web3_url: String, gas_adjuster_config: GasAdjusterConfig, pubdata_sending_mode: PubdataSendingMode, + base_token_fetcher: Arc, pubdata_pricing: Arc, ) -> Self { Self { @@ -44,6 +46,7 @@ impl GasAdjusterSingleton { gas_adjuster_config, pubdata_sending_mode, singleton: OnceCell::new(), + base_token_fetcher, pubdata_pricing, } } @@ -58,6 +61,7 @@ impl GasAdjusterSingleton { Arc::new(query_client.clone()), self.gas_adjuster_config, self.pubdata_sending_mode, + self.base_token_fetcher.clone(), self.pubdata_pricing.clone(), ) .await @@ -72,9 +76,12 @@ impl GasAdjusterSingleton { self, stop_signal: watch::Receiver, ) -> Option>> { - let gas_adjuster = self.singleton.get()?.clone(); + let gas_adjuster = match self.singleton.get()? { + Ok(gas_adjuster) => gas_adjuster.clone(), + Err(_e) => return None, + }; Some(tokio::spawn( - async move { gas_adjuster?.run(stop_signal).await }, + async move { gas_adjuster.run(stop_signal).await }, )) } } diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index 4dadf548d1b..33ca3157e28 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -60,6 +60,7 @@ use crate::{ tx_sender::{ApiContracts, TxSender, TxSenderBuilder, TxSenderConfig}, web3::{self, state::InternalApiConfig, Namespace}, }, + base_token_fetcher::{BaseTokenFetcher, ConversionRateFetcher, NoOpConversionRateFetcher}, basic_witness_input_producer::BasicWitnessInputProducer, commitment_generator::CommitmentGenerator, eth_sender::{ @@ -96,11 +97,13 @@ use crate::{ }; pub mod api_server; +pub mod base_token_fetcher; pub mod basic_witness_input_producer; pub mod block_reverter; pub mod commitment_generator; pub mod consensus; pub mod consistency_checker; +pub mod dev_api_conversion_rate; pub mod eth_sender; pub mod fee_model; pub mod gas_tracker; @@ -188,6 +191,10 @@ pub enum Component { Housekeeper, /// Component for exposing APIs to prover for providing proof generation data and accepting proofs. ProofDataHandler, + /// Base Token fetcher + BaseTokenFetcher, + /// Conversion rate API, for local development. + DevConversionRateApi, /// Component generating BFT consensus certificates for miniblocks. Consensus, /// Component generating commitment for L1 batches. @@ -226,6 +233,8 @@ impl FromStr for Components { "eth_tx_aggregator" => Ok(Components(vec![Component::EthTxAggregator])), "eth_tx_manager" => Ok(Components(vec![Component::EthTxManager])), "proof_data_handler" => Ok(Components(vec![Component::ProofDataHandler])), + "base_token_fetcher" => Ok(Components(vec![Component::BaseTokenFetcher])), + "dev_conversion_rate_api" => Ok(Components(vec![Component::DevConversionRateApi])), "consensus" => Ok(Components(vec![Component::Consensus])), "commitment_generator" => Ok(Components(vec![Component::CommitmentGenerator])), other => Err(format!("{} is not a valid component name", other)), @@ -307,22 +316,6 @@ pub async fn initialize_components( panic!("Circuit breaker triggered: {}", err); }); - let query_client = QueryClient::new(ð.web3_url).unwrap(); - let gas_adjuster_config = eth.gas_adjuster.context("gas_adjuster")?; - let sender = eth.sender.as_ref().context("sender")?; - let pubdata_pricing: Arc = - match genesis_config.l1_batch_commit_data_generator_mode { - L1BatchCommitDataGeneratorMode::Rollup => Arc::new(RollupPubdataPricing {}), - L1BatchCommitDataGeneratorMode::Validium => Arc::new(ValidiumPubdataPricing {}), - }; - - let mut gas_adjuster = GasAdjusterSingleton::new( - eth.web3_url.clone(), - gas_adjuster_config, - sender.pubdata_sending_mode, - pubdata_pricing, - ); - let (stop_sender, stop_receiver) = watch::channel(false); // Prometheus exporter and circuit breaker checker should run for every component configuration. @@ -348,6 +341,50 @@ pub async fn initialize_components( tokio::spawn(circuit_breaker_checker.run(stop_receiver.clone())), ]; + let query_client = QueryClient::new(ð.web3_url).unwrap(); + let gas_adjuster_config = eth.gas_adjuster.context("gas_adjuster")?; + let sender = eth.sender.as_ref().context("sender")?; + let pubdata_pricing: Arc = + match genesis_config.l1_batch_commit_data_generator_mode { + L1BatchCommitDataGeneratorMode::Rollup => Arc::new(RollupPubdataPricing {}), + L1BatchCommitDataGeneratorMode::Validium => Arc::new(ValidiumPubdataPricing {}), + }; + + if components.contains(&Component::DevConversionRateApi) { + let base_token_fetcher_config = configs + .base_token_fetcher + .clone() + .context("base_token_fetcher_config")?; + + let stop_receiver = stop_receiver.clone(); + let conversion_rate_task = tokio::spawn(async move { + dev_api_conversion_rate::run_server(stop_receiver, &base_token_fetcher_config).await + }); + task_futures.push(conversion_rate_task); + }; + + let base_token_fetcher = if components.contains(&Component::BaseTokenFetcher) { + Arc::new( + BaseTokenFetcher::new( + configs + .base_token_fetcher + .clone() + .context("base_token_fetcher_config")?, + ) + .await?, + ) as Arc + } else { + Arc::new(NoOpConversionRateFetcher::new()) + }; + + let mut gas_adjuster = GasAdjusterSingleton::new( + eth.web3_url.clone(), + gas_adjuster_config, + sender.pubdata_sending_mode, + base_token_fetcher, + pubdata_pricing, + ); + if components.contains(&Component::WsApi) || components.contains(&Component::HttpApi) || components.contains(&Component::ContractVerificationApi) diff --git a/core/lib/zksync_core/src/temp_config_store/mod.rs b/core/lib/zksync_core/src/temp_config_store/mod.rs index 2c5769f1f86..db4358c138e 100644 --- a/core/lib/zksync_core/src/temp_config_store/mod.rs +++ b/core/lib/zksync_core/src/temp_config_store/mod.rs @@ -2,6 +2,7 @@ use anyhow::Context as _; use zksync_config::{ configs::{ api::{HealthCheckConfig, MerkleTreeApiConfig, Web3JsonRpcConfig}, + base_token_fetcher::BaseTokenFetcherConfig, chain::{ CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, StateKeeperConfig, @@ -65,6 +66,7 @@ pub struct TempConfigStore { pub object_store_config: Option, pub observability: Option, pub snapshot_creator: Option, + pub base_token_fetcher: Option, } #[derive(Debug)] @@ -110,6 +112,7 @@ impl TempConfigStore { eth: self.eth_sender_config.clone(), snapshot_creator: self.snapshot_creator.clone(), observability: self.observability.clone(), + base_token_fetcher: self.base_token_fetcher.clone(), } } diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 5d956a306da..4a912f51548 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -16,6 +16,7 @@ use zksync_types::fee_model::FeeModelConfig; use crate::{ implementations::resources::{ + conversion_rate_fetcher::ConversionRateFetcherResource, eth_interface::EthInterfaceResource, fee_input::FeeInputResource, l1_tx_params::L1TxParamsResource, }, @@ -61,10 +62,15 @@ impl WiringLayer for SequencerL1GasLayer { L1BatchCommitDataGeneratorMode::Validium => Arc::new(ValidiumPubdataPricing {}), }; let client = context.get_resource::().await?.0; + let conversion_fetcher = context + .get_resource::() + .await? + .0; let adjuster = GasAdjuster::new( client, self.gas_adjuster_config, self.pubdata_sending_mode, + conversion_fetcher, pubdata_pricing, ) .await diff --git a/core/node/node_framework/src/implementations/resources/conversion_rate_fetcher.rs b/core/node/node_framework/src/implementations/resources/conversion_rate_fetcher.rs new file mode 100644 index 00000000000..b9fabb4f8cc --- /dev/null +++ b/core/node/node_framework/src/implementations/resources/conversion_rate_fetcher.rs @@ -0,0 +1,14 @@ +use std::sync::Arc; + +use zksync_core::base_token_fetcher::ConversionRateFetcher; + +use crate::resource::Resource; + +#[derive(Debug, Clone)] +pub struct ConversionRateFetcherResource(pub Arc); + +impl Resource for ConversionRateFetcherResource { + fn name() -> String { + "common/conversion_rate_fetcher".into() + } +} diff --git a/core/node/node_framework/src/implementations/resources/mod.rs b/core/node/node_framework/src/implementations/resources/mod.rs index 2225fcd2f4c..16c4d2718a3 100644 --- a/core/node/node_framework/src/implementations/resources/mod.rs +++ b/core/node/node_framework/src/implementations/resources/mod.rs @@ -1,5 +1,6 @@ pub mod action_queue; pub mod circuit_breakers; +pub mod conversion_rate_fetcher; pub mod eth_interface; pub mod fee_input; pub mod healthcheck; diff --git a/docs/guides/launch.md b/docs/guides/launch.md index 2889216dbbe..1c5496fa051 100644 --- a/docs/guides/launch.md +++ b/docs/guides/launch.md @@ -94,6 +94,39 @@ template. Make sure you have environment variables set right, you can check it by running: `zk env`. You should see `* dev` in output. +## Gas Price Oracle + +As prices in a custom base token setting need to be in the native currency of the chain. These prices are partially +determined by l1 gas prices in L1, which are in eth, so a conversion is required. + +### Conversion rate API + +To use a conversion rate oracle, you will need to add the component to the command: + +``` +zk server --components "api,tree,eth,state_keeper,housekeeper,commitment_generator,base_token_fetcher" +``` + +In order to run the server with the conversion rate, you will need to configure the variables in the +`etc/env/base/base_token_fetcher.toml` file, or you can export the variables in the environment: + +``` +export BASE_TOKEN_FETCHER_HOST= +# Poll interval is in seconds +export BASE_TOKEN_FETCHER_POLL_INTERVAL= +export BASE_TOKEN_FETCHER_TOKEN_ADDRESS= +``` + +Additionally, you can run the server with the `dev_conversion_rate_api` component to enable the development conversion +rate API, this component is only meant for development purposes and should not be used in production and will provide a +fixed conversion rate for all tokens: + +``` +zk server --components "api,tree,eth,state_keeper,housekeeper,commitment_generator,base_token_fetcher,dev_conversion_rate_api" +``` + +This server will use the host setted in the environment variable previously mentioned. + ## Running server using Google cloud storage object store instead of default In memory store Get the service_account.json file containing the GCP credentials from kubernetes secret for relevant environment(stage2/ diff --git a/etc/env/base/base_token_fetcher.toml b/etc/env/base/base_token_fetcher.toml new file mode 100644 index 00000000000..4910110b7c7 --- /dev/null +++ b/etc/env/base/base_token_fetcher.toml @@ -0,0 +1,5 @@ +[base_token_fetcher] +host="http://127.0.0.1:5000" +# Poll interval is in seconds +poll_interval=10 +token_address="0xdF2Ff03eAAfae59fA7fd30D4f91ab045B3d5D95D" diff --git a/prover/prover_dal/sqlx-data.json b/prover/prover_dal/sqlx-data.json new file mode 100644 index 00000000000..95c8c858baa --- /dev/null +++ b/prover/prover_dal/sqlx-data.json @@ -0,0 +1,3 @@ +{ + "db": "PostgreSQL" +} \ No newline at end of file