diff --git a/core/bin/zksync_api/src/fee_ticker/fee_token_validator.rs b/core/bin/zksync_api/src/fee_ticker/fee_token_validator.rs index 592a1d9905..2aed3e5af6 100644 --- a/core/bin/zksync_api/src/fee_ticker/fee_token_validator.rs +++ b/core/bin/zksync_api/src/fee_ticker/fee_token_validator.rs @@ -2,7 +2,7 @@ //! an entity which decides whether certain ERC20 token is suitable for paying fees. // Built-in uses -use std::{collections::HashMap, str::FromStr}; +use std::collections::{HashMap, HashSet}; // Workspace uses use zksync_types::{ tokens::{Token, TokenLike}, @@ -15,12 +15,18 @@ use crate::utils::token_db_cache::TokenDBCache; #[derive(Debug, Clone)] pub(crate) struct FeeTokenValidator { tokens_cache: TokenCacheWrapper, + /// List of tokens that aren't accepted to pay fees in. + disabled_tokens: HashSet
, } impl FeeTokenValidator { - pub(crate) fn new(cache: impl Into) -> Self { + pub(crate) fn new( + cache: impl Into, + disabled_tokens: HashSet
, + ) -> Self { Self { tokens_cache: cache.into(), + disabled_tokens, } } @@ -40,12 +46,8 @@ impl FeeTokenValidator { // Later we'll check Uniswap trading volume for tokens. That's why this function is already `async` even // though it's not really `async` at this moment. - let not_supported_tokens = &[ - Address::from_str("38A2fDc11f526Ddd5a607C1F251C065f40fBF2f7").unwrap(), // PHNX (PhoenixDAO) - ]; - if let Some(token) = token { - let not_acceptable = not_supported_tokens.iter().any(|&t| t == token.address); + let not_acceptable = self.disabled_tokens.contains(&token.address); Ok(!not_acceptable) } else { // Unknown tokens aren't suitable for our needs, obviously. @@ -84,6 +86,8 @@ impl TokenCacheWrapper { #[cfg(test)] mod tests { use super::*; + use std::collections::HashSet; + use std::str::FromStr; #[tokio::test] async fn check_tokens() { @@ -98,7 +102,10 @@ mod tests { tokens.insert(TokenLike::Address(dai_token_address), dai_token); tokens.insert(TokenLike::Address(phnx_token_address), phnx_token); - let validator = FeeTokenValidator::new(tokens); + let mut disabled_tokens = HashSet::new(); + disabled_tokens.insert(phnx_token_address); + + let validator = FeeTokenValidator::new(tokens, disabled_tokens); let dai_allowed = validator .token_allowed(TokenLike::Address(dai_token_address)) diff --git a/core/bin/zksync_api/src/fee_ticker/mod.rs b/core/bin/zksync_api/src/fee_ticker/mod.rs index 5ed6e76c03..e978165ca2 100644 --- a/core/bin/zksync_api/src/fee_ticker/mod.rs +++ b/core/bin/zksync_api/src/fee_ticker/mod.rs @@ -4,7 +4,7 @@ //! `( zkp cost of chunk * number of chunks + gas price of transaction) * token risk factor / cost of token is usd` // Built-in deps -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; // External deps use bigdecimal::BigDecimal; use futures::{ @@ -19,7 +19,7 @@ use num::{ use serde::{Deserialize, Serialize}; use tokio::task::JoinHandle; // Workspace deps -use zksync_config::TokenPriceSource; +use zksync_config::{FeeTickerOptions, TokenPriceSource}; use zksync_storage::ConnectionPool; use zksync_types::{ Address, ChangePubKeyOp, Token, TokenId, TokenLike, TransferOp, TransferToNewOp, TxFeeTypes, @@ -142,7 +142,7 @@ pub struct TickerConfig { zkp_cost_chunk_usd: Ratio, gas_cost_tx: GasOperationsCost, tokens_risk_factors: HashMap>, - not_subsidized_tokens: Vec
, + not_subsidized_tokens: HashSet
, } #[derive(Debug, PartialEq, Eq)] @@ -180,28 +180,27 @@ struct FeeTicker { #[must_use] pub fn run_ticker_task( - token_price_source: TokenPriceSource, - not_subsidized_tokens: Vec
, - fast_processing_coeff: f64, db_pool: ConnectionPool, tricker_requests: Receiver, ) -> JoinHandle<()> { + let config = FeeTickerOptions::from_env(); + let ticker_config = TickerConfig { zkp_cost_chunk_usd: Ratio::from_integer(BigUint::from(10u32).pow(3u32)).inv(), - gas_cost_tx: GasOperationsCost::from_constants(fast_processing_coeff), + gas_cost_tx: GasOperationsCost::from_constants(config.fast_processing_coeff), tokens_risk_factors: HashMap::new(), - not_subsidized_tokens, + not_subsidized_tokens: config.not_subsidized_tokens, }; let cache = TokenDBCache::new(db_pool.clone()); - let validator = FeeTokenValidator::new(cache); + let validator = FeeTokenValidator::new(cache, config.disabled_tokens); let client = reqwest::ClientBuilder::new() .timeout(CONNECTION_TIMEOUT) .connect_timeout(CONNECTION_TIMEOUT) .build() .expect("Failed to build reqwest::Client"); - match token_price_source { + match config.token_price_source { TokenPriceSource::CoinMarketCap { base_url } => { let token_price_api = CoinMarketCapAPI::new(client, base_url); @@ -309,10 +308,7 @@ impl FeeTicker { /// Returns `true` if the token is subsidized. async fn is_token_subsidized(&mut self, token: Token) -> bool { - self.config - .not_subsidized_tokens - .iter() - .all(|&t| t != token.address) + !self.config.not_subsidized_tokens.contains(&token.address) } async fn get_fee_from_ticker_in_wei( diff --git a/core/bin/zksync_api/src/fee_ticker/tests.rs b/core/bin/zksync_api/src/fee_ticker/tests.rs index ec7d99d811..776765ad0f 100644 --- a/core/bin/zksync_api/src/fee_ticker/tests.rs +++ b/core/bin/zksync_api/src/fee_ticker/tests.rs @@ -89,7 +89,9 @@ fn get_test_ticker_config() -> TickerConfig { .collect(), not_subsidized_tokens: vec![ Address::from_str("34083bbd70d394110487feaa087da875a54624ec").unwrap(), - ], + ] + .into_iter() + .collect(), } } @@ -155,7 +157,7 @@ fn format_with_dot(num: &Ratio, precision: usize) -> String { #[test] fn test_ticker_formula() { - let validator = FeeTokenValidator::new(HashMap::new()); + let validator = FeeTokenValidator::new(HashMap::new(), Default::default()); let config = get_test_ticker_config(); let mut ticker = FeeTicker::new( @@ -251,7 +253,7 @@ fn test_ticker_formula() { #[test] fn test_fee_for_unsubsidized_tokens() { - let validator = FeeTokenValidator::new(HashMap::new()); + let validator = FeeTokenValidator::new(HashMap::new(), Default::default()); let config = get_test_ticker_config(); let mut ticker = FeeTicker::new( diff --git a/core/bin/zksync_api/src/lib.rs b/core/bin/zksync_api/src/lib.rs index 43236ebc01..78d7b63084 100644 --- a/core/bin/zksync_api/src/lib.rs +++ b/core/bin/zksync_api/src/lib.rs @@ -2,7 +2,7 @@ use crate::{api_server::start_api_server, fee_ticker::run_ticker_task}; use futures::channel::mpsc; -use zksync_config::{AdminServerOptions, ConfigurationOptions, FeeTickerOptions}; +use zksync_config::{AdminServerOptions, ConfigurationOptions}; use zksync_storage::ConnectionPool; pub mod api_server; @@ -23,15 +23,8 @@ pub fn run_api( let config_options = ConfigurationOptions::from_env(); let admin_server_options = AdminServerOptions::from_env(); - let fee_ticker_options = FeeTickerOptions::from_env(); - let ticker_task = run_ticker_task( - config_options.token_price_source.clone(), - fee_ticker_options.not_subsidized_tokens, - config_options.ticker_fast_processing_coeff, - connection_pool.clone(), - ticker_request_receiver, - ); + let ticker_task = run_ticker_task(connection_pool.clone(), ticker_request_receiver); start_api_server( connection_pool, diff --git a/core/lib/config/src/lib.rs b/core/lib/config/src/lib.rs index 111bbebdd8..4dfc1ac6f5 100644 --- a/core/lib/config/src/lib.rs +++ b/core/lib/config/src/lib.rs @@ -1,9 +1,9 @@ // Built-in deps -use std::{env, net::SocketAddr, str::FromStr, time::Duration}; +use std::{collections::HashSet, env, net::SocketAddr, str::FromStr, time::Duration}; // External uses use url::Url; // Workspace uses -use zksync_basic_types::{H160, H256}; +use zksync_basic_types::{Address, H256}; use zksync_utils::{get_env, parse_env, parse_env_if_exists, parse_env_with}; // Local uses @@ -133,6 +133,37 @@ impl MiniblockTimings { } } +/// Configuration options related to fee ticker. +#[derive(Debug)] +pub struct FeeTickerOptions { + /// Source to fetch token prices from (e.g. CoinGecko or coinmarketcap). + pub token_price_source: TokenPriceSource, + /// Fee increase coefficient for fast processing of withdrawal. + pub fast_processing_coeff: f64, + /// List of the tokens that aren't acceptable for paying fee in. + pub disabled_tokens: HashSet
, + /// Tokens for which subsidies are disabled. + pub not_subsidized_tokens: HashSet
, +} + +impl FeeTickerOptions { + fn comma_separated_addresses(name: &str) -> HashSet
{ + get_env(name) + .split(',') + .map(|p| p.parse().unwrap()) + .collect() + } + + pub fn from_env() -> Self { + Self { + token_price_source: TokenPriceSource::from_env(), + fast_processing_coeff: parse_env("TICKER_FAST_PROCESSING_COEFF"), + disabled_tokens: Self::comma_separated_addresses("TICKER_DISABLED_TOKENS"), + not_subsidized_tokens: Self::comma_separated_addresses("NOT_SUBSIDIZED_TOKENS"), + } + } +} + #[derive(Debug, Clone)] pub struct ConfigurationOptions { pub rest_api_server_address: SocketAddr, @@ -142,10 +173,10 @@ pub struct ConfigurationOptions { pub core_server_url: String, pub web3_url: String, pub genesis_tx_hash: H256, - pub contract_eth_addr: H160, - pub governance_eth_addr: H160, - pub operator_fee_eth_addr: H160, - pub operator_commit_eth_addr: H160, + pub contract_eth_addr: Address, + pub governance_eth_addr: Address, + pub operator_fee_eth_addr: Address, + pub operator_commit_eth_addr: Address, pub operator_private_key: Option, pub chain_id: u8, pub gas_price_factor: f64, @@ -159,10 +190,7 @@ pub struct ConfigurationOptions { pub idle_provers: u32, pub miniblock_timings: MiniblockTimings, pub prometheus_export_port: u16, - pub token_price_source: TokenPriceSource, pub witness_generators: usize, - /// Fee increase coefficient for fast processing of withdrawal. - pub ticker_fast_processing_coeff: f64, pub forced_exit_minimum_account_age: Duration, pub enforce_pubkey_change_fee: bool, } @@ -213,9 +241,7 @@ impl ConfigurationOptions { idle_provers: parse_env("IDLE_PROVERS"), miniblock_timings: MiniblockTimings::from_env(), prometheus_export_port: parse_env("PROMETHEUS_EXPORT_PORT"), - token_price_source: TokenPriceSource::from_env(), witness_generators: parse_env("WITNESS_GENERATORS"), - ticker_fast_processing_coeff: parse_env("TICKER_FAST_PROCESSING_COEFF"), forced_exit_minimum_account_age, enforce_pubkey_change_fee: parse_env_if_exists("ENFORCE_PUBKEY_CHANGE_FEE") .unwrap_or(true), @@ -251,20 +277,3 @@ impl AvailableBlockSizesConfig { result } } - -/// Configuration options related to FeeTicker. -#[derive(Debug)] -pub struct FeeTickerOptions { - pub not_subsidized_tokens: Vec, -} - -impl FeeTickerOptions { - pub fn from_env() -> Self { - Self { - not_subsidized_tokens: get_env("NOT_SUBSIDIZED_TOKENS") - .split(',') - .map(|p| p.parse().unwrap()) - .collect(), - } - } -} diff --git a/etc/env/dev.env.example b/etc/env/dev.env.example index 7cefefc08a..2c5da5138d 100755 --- a/etc/env/dev.env.example +++ b/etc/env/dev.env.example @@ -180,3 +180,7 @@ FEE_ACCOUNT_PRIVATE_KEY=unset # Set of token addresses for which no subsidies are provided. NOT_SUBSIDIZED_TOKENS=2b591e99afe9f32eaa6214f7b7629768c40eeb39,34083bbd70d394110487feaa087da875a54624ec + +# Set of token addresses which are not acceptable in the ticker for paying fees in. +# Should be a comma-separated list. +TICKER_DISABLED_TOKENS=38A2fDc11f526Ddd5a607C1F251C065f40fBF2f7