-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(main): base token gas oracle switch conversion rate to BigDecimal
#220
Changes from all commits
fa90d16
2c17607
b5d9231
ac3c3e3
c199c4e
8f98dbe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,15 @@ | ||
use std::{cmp::min, sync::Arc, time::Duration}; | ||
use std::{ | ||
cmp::min, | ||
str::FromStr, | ||
sync::{Arc, Mutex}, | ||
time::Duration, | ||
}; | ||
|
||
use anyhow::Context; | ||
use async_trait::async_trait; | ||
use bigdecimal::BigDecimal; | ||
use hex::ToHex; | ||
use metrics::atomics::AtomicU64; | ||
use tokio::{sync::Mutex, time::sleep}; | ||
use tokio::time::sleep; | ||
use zksync_config::configs::BaseTokenFetcherConfig; | ||
|
||
const MAX_CONVERSION_RATE_FETCH_RETRIES: u8 = 10; | ||
|
@@ -13,7 +18,7 @@ const MAX_CONVERSION_RATE_FETCH_RETRIES: u8 = 10; | |
/// 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<u64>; | ||
fn conversion_rate(&self) -> anyhow::Result<BigDecimal>; | ||
async fn update(&self) -> anyhow::Result<()>; | ||
} | ||
|
||
|
@@ -32,8 +37,8 @@ impl NoOpConversionRateFetcher { | |
|
||
#[async_trait] | ||
impl ConversionRateFetcher for NoOpConversionRateFetcher { | ||
fn conversion_rate(&self) -> anyhow::Result<u64> { | ||
Ok(1) | ||
fn conversion_rate(&self) -> anyhow::Result<BigDecimal> { | ||
Ok(BigDecimal::from(1)) | ||
} | ||
|
||
async fn update(&self) -> anyhow::Result<()> { | ||
|
@@ -46,7 +51,7 @@ impl ConversionRateFetcher for NoOpConversionRateFetcher { | |
#[derive(Debug)] | ||
pub struct BaseTokenFetcher { | ||
pub config: BaseTokenFetcherConfig, | ||
pub latest_to_eth_conversion_rate: AtomicU64, | ||
pub latest_to_eth_conversion_rate: Arc<Mutex<BigDecimal>>, | ||
http_client: reqwest::Client, | ||
error_reporter: Arc<Mutex<ErrorReporter>>, | ||
} | ||
|
@@ -55,23 +60,25 @@ impl BaseTokenFetcher { | |
pub async fn new(config: BaseTokenFetcherConfig) -> anyhow::Result<Self> { | ||
let http_client = reqwest::Client::new(); | ||
|
||
let conversion_rate = http_client | ||
let conversion_rate_str = http_client | ||
.get(format!( | ||
"{}/conversion_rate/0x{}", | ||
config.host, | ||
config.token_address.encode_hex::<String>() | ||
)) | ||
.send() | ||
.await? | ||
.json::<u64>() | ||
.json::<String>() | ||
.await | ||
.context("Unable to parse the response of the native token conversion rate server")?; | ||
let conversion_rate = BigDecimal::from_str(&conversion_rate_str) | ||
.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), | ||
latest_to_eth_conversion_rate: Arc::new(Mutex::new(conversion_rate)), | ||
http_client, | ||
error_reporter, | ||
}) | ||
|
@@ -81,9 +88,9 @@ impl BaseTokenFetcher { | |
pub async fn new_with_timeout(config: BaseTokenFetcherConfig) -> anyhow::Result<Self> { | ||
let http_client = reqwest::Client::new(); | ||
|
||
let mut conversion_rate = None; | ||
let mut conversion_rate_str = None; | ||
let mut tries = 0; | ||
while conversion_rate.is_none() && tries < MAX_CONVERSION_RATE_FETCH_RETRIES { | ||
while conversion_rate_str.is_none() && tries < MAX_CONVERSION_RATE_FETCH_RETRIES { | ||
match http_client | ||
.get(format!( | ||
"{}/conversion_rate/0x{}", | ||
|
@@ -94,7 +101,7 @@ impl BaseTokenFetcher { | |
.await | ||
{ | ||
Ok(res) => { | ||
conversion_rate = Some(res.json::<u64>().await.context( | ||
conversion_rate_str = Some(res.json::<String>().await.context( | ||
"Unable to parse the response of the native token conversion rate server", | ||
)?); | ||
} | ||
|
@@ -105,12 +112,14 @@ impl BaseTokenFetcher { | |
} | ||
} | ||
|
||
let conversion_rate = conversion_rate | ||
let conversion_rate = conversion_rate_str | ||
.ok_or_else(|| anyhow::anyhow!("Failed to fetch the native token conversion rate"))?; | ||
let conversion_rate = BigDecimal::from_str(&conversion_rate) | ||
.context("Unable to parse the response of the native token conversion rate server")?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Following my comment above, this from_str returns a result, right? |
||
let error_reporter = Arc::new(Mutex::new(ErrorReporter::new())); | ||
Ok(Self { | ||
config, | ||
latest_to_eth_conversion_rate: AtomicU64::new(conversion_rate), | ||
latest_to_eth_conversion_rate: Arc::new(Mutex::new(conversion_rate)), | ||
http_client, | ||
error_reporter, | ||
}) | ||
|
@@ -119,11 +128,8 @@ impl BaseTokenFetcher { | |
|
||
#[async_trait] | ||
impl ConversionRateFetcher for BaseTokenFetcher { | ||
fn conversion_rate(&self) -> anyhow::Result<u64> { | ||
anyhow::Ok( | ||
self.latest_to_eth_conversion_rate | ||
.load(std::sync::atomic::Ordering::Relaxed), | ||
) | ||
fn conversion_rate(&self) -> anyhow::Result<BigDecimal> { | ||
Ok(self.latest_to_eth_conversion_rate.lock().unwrap().clone()) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we could return a reference to avoid cloning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, shouldn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have a point here, but the related trait returns a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cloning bigdecimal is cheap, so I see no problem here. |
||
|
||
async fn update(&self) -> anyhow::Result<()> { | ||
|
@@ -138,17 +144,20 @@ impl ConversionRateFetcher for BaseTokenFetcher { | |
.await | ||
{ | ||
Ok(response) => { | ||
let conversion_rate = response.json::<u64>().await.context( | ||
let conversion_rate_str = response.json::<String>().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(); | ||
*self.latest_to_eth_conversion_rate.lock().unwrap() = | ||
BigDecimal::from_str(&conversion_rate_str).context( | ||
"Unable to parse the response of the native token conversion rate server", | ||
)?; | ||
|
||
self.error_reporter.lock().unwrap().reset(); | ||
} | ||
Err(err) => self | ||
.error_reporter | ||
.lock() | ||
.await | ||
.unwrap() | ||
.process(anyhow::anyhow!(err)), | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ use std::{ | |
use tokio::sync::watch; | ||
use zksync_base_token_fetcher::ConversionRateFetcher; | ||
use zksync_config::{configs::eth_sender::PubdataSendingMode, GasAdjusterConfig}; | ||
use zksync_dal::BigDecimal; | ||
use zksync_eth_client::{Error, EthInterface}; | ||
use zksync_types::{commitment::L1BatchCommitmentMode, L1_GAS_PER_PUBDATA_BYTE, U256, U64}; | ||
use zksync_web3_decl::client::{DynClient, L1}; | ||
|
@@ -178,9 +179,21 @@ impl GasAdjuster { | |
let calculated_price = | ||
(self.config.internal_l1_pricing_multiplier * effective_gas_price as f64) as u64; | ||
|
||
let conversion_rate = self.base_token_fetcher.conversion_rate().unwrap_or(1); | ||
let conversion_rate = self | ||
.base_token_fetcher | ||
.conversion_rate() | ||
.unwrap_or(BigDecimal::from(1)); | ||
let bound_gas_price = BigDecimal::from(self.bound_gas_price(calculated_price)); | ||
|
||
self.bound_gas_price(calculated_price) * conversion_rate | ||
U256::from_dec_str( | ||
&match (conversion_rate * bound_gas_price).round(0) { | ||
zero if zero == BigDecimal::from(0) => BigDecimal::from(1), | ||
val => val, | ||
} | ||
.to_string(), | ||
) | ||
.unwrap() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we return a Result<T, Err> instead of unwrapping? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently we If we were to return a |
||
.as_u64() | ||
} | ||
|
||
pub(crate) fn estimate_effective_pubdata_price(&self) -> u64 { | ||
|
@@ -207,9 +220,22 @@ impl GasAdjuster { | |
* BLOB_GAS_PER_BYTE as f64 | ||
* self.config.internal_pubdata_pricing_multiplier; | ||
|
||
let conversion_rate = self.base_token_fetcher.conversion_rate().unwrap_or(1); | ||
|
||
self.bound_blob_base_fee(calculated_price) * conversion_rate | ||
let conversion_rate = self | ||
.base_token_fetcher | ||
.conversion_rate() | ||
.unwrap_or(BigDecimal::from(1)); | ||
let bound_blob_base_fee = | ||
BigDecimal::from(self.bound_blob_base_fee(calculated_price)); | ||
|
||
U256::from_dec_str( | ||
&match (conversion_rate * bound_blob_base_fee).round(0) { | ||
zero if zero == BigDecimal::from(0) => BigDecimal::from(1), | ||
val => val, | ||
} | ||
.to_string(), | ||
) | ||
.unwrap() | ||
.as_u64() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as above |
||
} | ||
PubdataSendingMode::Calldata => { | ||
self.estimate_effective_gas_price() * self.pubdata_byte_gas() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
Maybe we could also return what was the response from the server,
it can be annoying to see this error and don't know that the server is responding.
Otherwise, this depends more on the caller properly reporting the error and not the callee (the gas adjuster), so maybe it's not really required.