mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-30 01:28:21 -05:00
feat: gas price oracle (#2600)
This commit is contained in:
@@ -86,6 +86,9 @@ pub enum ProviderError {
|
||||
/// Thrown when the cache service task dropped
|
||||
#[error("cache service task stopped")]
|
||||
CacheServiceUnavailable,
|
||||
/// Thrown when the gas oracle task dropped
|
||||
#[error("gas oracle task stopped")]
|
||||
GasPriceOracleServiceUnavailable,
|
||||
/// Thrown when we failed to lookup a block for the pending state
|
||||
#[error("Unknown block hash: {0:}")]
|
||||
UnknownBlockHash(H256),
|
||||
|
||||
@@ -14,8 +14,8 @@ use reth_provider::{
|
||||
BlockProviderIdExt, EvmEnvProvider, HeaderProvider, ReceiptProviderIdExt, StateProviderFactory,
|
||||
};
|
||||
use reth_rpc::{
|
||||
eth::cache::EthStateCache, AuthLayer, Claims, EngineEthApi, EthApi, EthFilter,
|
||||
JwtAuthValidator, JwtSecret,
|
||||
eth::{cache::EthStateCache, gas_oracle::GasPriceOracle},
|
||||
AuthLayer, Claims, EngineEthApi, EthApi, EthFilter, JwtAuthValidator, JwtSecret,
|
||||
};
|
||||
use reth_rpc_api::{servers::*, EngineApiServer};
|
||||
use reth_tasks::TaskSpawner;
|
||||
@@ -51,8 +51,9 @@ where
|
||||
EngineApi: EngineApiServer,
|
||||
{
|
||||
// spawn a new cache task
|
||||
let eth_cache = EthStateCache::spawn_with(client.clone(), Default::default(), executor);
|
||||
let eth_api = EthApi::new(client.clone(), pool.clone(), network, eth_cache.clone());
|
||||
let eth_cache = EthStateCache::spawn_with(client.clone(), Default::default(), executor.clone());
|
||||
let gas_oracle = GasPriceOracle::new(client.clone(), Default::default(), eth_cache.clone());
|
||||
let eth_api = EthApi::new(client.clone(), pool.clone(), network, eth_cache.clone(), gas_oracle);
|
||||
let eth_filter = EthFilter::new(client, pool, eth_cache.clone(), DEFAULT_MAX_LOGS_IN_RESPONSE);
|
||||
launch_with_eth_api(eth_api, eth_filter, engine_api, socket_addr, secret).await
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use reth_rpc::{
|
||||
eth::cache::{EthStateCache, EthStateCacheConfig},
|
||||
eth::{
|
||||
cache::{EthStateCache, EthStateCacheConfig},
|
||||
gas_oracle::GasPriceOracleConfig,
|
||||
},
|
||||
EthApi, EthFilter, EthPubSub,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -25,6 +28,8 @@ pub struct EthHandlers<Client, Pool, Network, Events> {
|
||||
pub struct EthConfig {
|
||||
/// Settings for the caching layer
|
||||
pub cache: EthStateCacheConfig,
|
||||
/// Settings for the gas price oracle
|
||||
pub gas_oracle: GasPriceOracleConfig,
|
||||
/// The maximum number of tracing calls that can be executed in concurrently.
|
||||
pub max_tracing_requests: usize,
|
||||
/// Maximum number of logs that can be returned in a single response in `eth_getLogs` calls.
|
||||
@@ -35,6 +40,7 @@ impl Default for EthConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cache: EthStateCacheConfig::default(),
|
||||
gas_oracle: GasPriceOracleConfig::default(),
|
||||
max_tracing_requests: 10,
|
||||
max_logs_per_response: DEFAULT_MAX_LOGS_IN_RESPONSE,
|
||||
}
|
||||
|
||||
@@ -110,8 +110,9 @@ use reth_provider::{
|
||||
StateProviderFactory,
|
||||
};
|
||||
use reth_rpc::{
|
||||
eth::cache::EthStateCache, AdminApi, DebugApi, EngineEthApi, EthApi, EthFilter, EthPubSub,
|
||||
EthSubscriptionIdProvider, NetApi, TraceApi, TracingCallGuard, TxPoolApi, Web3Api,
|
||||
eth::{cache::EthStateCache, gas_oracle::GasPriceOracle},
|
||||
AdminApi, DebugApi, EngineEthApi, EthApi, EthFilter, EthPubSub, EthSubscriptionIdProvider,
|
||||
NetApi, TraceApi, TracingCallGuard, TxPoolApi, Web3Api,
|
||||
};
|
||||
use reth_rpc_api::{servers::*, EngineApiServer};
|
||||
use reth_tasks::TaskSpawner;
|
||||
@@ -826,6 +827,11 @@ where
|
||||
self.config.eth.cache.clone(),
|
||||
self.executor.clone(),
|
||||
);
|
||||
let gas_oracle = GasPriceOracle::new(
|
||||
self.client.clone(),
|
||||
self.config.eth.gas_oracle.clone(),
|
||||
cache.clone(),
|
||||
);
|
||||
let new_canonical_blocks = self.events.canonical_state_stream();
|
||||
let c = cache.clone();
|
||||
self.executor.spawn_critical(
|
||||
@@ -840,6 +846,7 @@ where
|
||||
self.pool.clone(),
|
||||
self.network.clone(),
|
||||
cache.clone(),
|
||||
gas_oracle,
|
||||
);
|
||||
let filter = EthFilter::new(
|
||||
self.client.clone(),
|
||||
|
||||
@@ -97,8 +97,8 @@ where
|
||||
EthApiClient::send_transaction(client, transaction_request).await.unwrap_err();
|
||||
EthApiClient::hashrate(client).await.unwrap();
|
||||
EthApiClient::submit_hashrate(client, U256::default(), H256::default()).await.unwrap();
|
||||
EthApiClient::gas_price(client).await.unwrap();
|
||||
EthApiClient::max_priority_fee_per_gas(client).await.unwrap();
|
||||
EthApiClient::gas_price(client).await.unwrap_err();
|
||||
EthApiClient::max_priority_fee_per_gas(client).await.unwrap_err();
|
||||
|
||||
// Unimplemented
|
||||
assert!(is_unimplemented(
|
||||
|
||||
@@ -30,8 +30,7 @@ where
|
||||
|
||||
/// Returns a suggestion for the priority fee (the tip)
|
||||
pub(crate) async fn suggested_priority_fee(&self) -> EthResult<U256> {
|
||||
// TODO: properly implement sampling https://github.com/ethereum/pm/issues/328#issuecomment-853234014
|
||||
Ok(U256::from(1e9 as u64))
|
||||
self.gas_oracle().suggest_tip_cap().await
|
||||
}
|
||||
|
||||
/// Reports the fee history, for the given amount of blocks, up until the newest block
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
use crate::eth::{
|
||||
cache::EthStateCache,
|
||||
error::{EthApiError, EthResult},
|
||||
gas_oracle::GasPriceOracle,
|
||||
signer::EthSigner,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
@@ -71,8 +72,21 @@ pub struct EthApi<Client, Pool, Network> {
|
||||
|
||||
impl<Client, Pool, Network> EthApi<Client, Pool, Network> {
|
||||
/// Creates a new, shareable instance.
|
||||
pub fn new(client: Client, pool: Pool, network: Network, eth_cache: EthStateCache) -> Self {
|
||||
let inner = EthApiInner { client, pool, network, signers: Default::default(), eth_cache };
|
||||
pub fn new(
|
||||
client: Client,
|
||||
pool: Pool,
|
||||
network: Network,
|
||||
eth_cache: EthStateCache,
|
||||
gas_oracle: GasPriceOracle<Client>,
|
||||
) -> Self {
|
||||
let inner = EthApiInner {
|
||||
client,
|
||||
pool,
|
||||
network,
|
||||
signers: Default::default(),
|
||||
eth_cache,
|
||||
gas_oracle,
|
||||
};
|
||||
Self {
|
||||
inner: Arc::new(inner),
|
||||
fee_history_cache: FeeHistoryCache::new(
|
||||
@@ -86,6 +100,11 @@ impl<Client, Pool, Network> EthApi<Client, Pool, Network> {
|
||||
&self.inner.eth_cache
|
||||
}
|
||||
|
||||
/// Returns the gas oracle frontend
|
||||
pub(crate) fn gas_oracle(&self) -> &GasPriceOracle<Client> {
|
||||
&self.inner.gas_oracle
|
||||
}
|
||||
|
||||
/// Returns the inner `Client`
|
||||
pub fn client(&self) -> &Client {
|
||||
&self.inner.client
|
||||
@@ -238,4 +257,6 @@ struct EthApiInner<Client, Pool, Network> {
|
||||
signers: Vec<Box<dyn EthSigner>>,
|
||||
/// The async cache frontend for eth related data
|
||||
eth_cache: EthStateCache,
|
||||
/// The async gas oracle frontend for gas price suggestions
|
||||
gas_oracle: GasPriceOracle<Client>,
|
||||
}
|
||||
|
||||
@@ -364,7 +364,10 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{eth::cache::EthStateCache, EthApi};
|
||||
use crate::{
|
||||
eth::{cache::EthStateCache, gas_oracle::GasPriceOracle},
|
||||
EthApi,
|
||||
};
|
||||
use jsonrpsee::types::error::INVALID_PARAMS_CODE;
|
||||
use rand::random;
|
||||
use reth_network_api::test_utils::NoopNetwork;
|
||||
@@ -376,11 +379,13 @@ mod tests {
|
||||
#[tokio::test]
|
||||
/// Handler for: `eth_test_fee_history`
|
||||
async fn test_fee_history() {
|
||||
let cache = EthStateCache::spawn(NoopProvider::default(), Default::default());
|
||||
let eth_api = EthApi::new(
|
||||
NoopProvider::default(),
|
||||
testing_pool(),
|
||||
NoopNetwork,
|
||||
EthStateCache::spawn(NoopProvider::default(), Default::default()),
|
||||
cache.clone(),
|
||||
GasPriceOracle::new(NoopProvider::default(), Default::default(), cache),
|
||||
);
|
||||
|
||||
let response = <EthApi<_, _, _> as EthApiServer>::fee_history(
|
||||
@@ -460,11 +465,13 @@ mod tests {
|
||||
|
||||
gas_used_ratios.pop();
|
||||
|
||||
let cache = EthStateCache::spawn(mock_provider.clone(), Default::default());
|
||||
let eth_api = EthApi::new(
|
||||
mock_provider,
|
||||
mock_provider.clone(),
|
||||
testing_pool(),
|
||||
NoopNetwork,
|
||||
EthStateCache::spawn(NoopProvider::default(), Default::default()),
|
||||
cache.clone(),
|
||||
GasPriceOracle::new(mock_provider, Default::default(), cache.clone()),
|
||||
);
|
||||
|
||||
let response = <EthApi<_, _, _> as EthApiServer>::fee_history(
|
||||
|
||||
@@ -145,7 +145,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::eth::cache::EthStateCache;
|
||||
use crate::eth::{cache::EthStateCache, gas_oracle::GasPriceOracle};
|
||||
use reth_primitives::{StorageKey, StorageValue};
|
||||
use reth_provider::test_utils::{ExtendedAccount, MockEthProvider, NoopProvider};
|
||||
use reth_transaction_pool::test_utils::testing_pool;
|
||||
@@ -156,11 +156,13 @@ mod tests {
|
||||
// === Noop ===
|
||||
let pool = testing_pool();
|
||||
|
||||
let cache = EthStateCache::spawn(NoopProvider::default(), Default::default());
|
||||
let eth_api = EthApi::new(
|
||||
NoopProvider::default(),
|
||||
pool.clone(),
|
||||
(),
|
||||
EthStateCache::spawn(NoopProvider::default(), Default::default()),
|
||||
cache.clone(),
|
||||
GasPriceOracle::new(NoopProvider::default(), Default::default(), cache),
|
||||
);
|
||||
let address = Address::random();
|
||||
let storage = eth_api.storage_at(address, U256::ZERO.into(), None).unwrap();
|
||||
@@ -174,11 +176,13 @@ mod tests {
|
||||
let account = ExtendedAccount::new(0, U256::ZERO).extend_storage(storage);
|
||||
mock_provider.add_account(address, account);
|
||||
|
||||
let cache = EthStateCache::spawn(mock_provider.clone(), Default::default());
|
||||
let eth_api = EthApi::new(
|
||||
mock_provider.clone(),
|
||||
pool,
|
||||
(),
|
||||
EthStateCache::spawn(mock_provider, Default::default()),
|
||||
cache.clone(),
|
||||
GasPriceOracle::new(mock_provider, Default::default(), cache),
|
||||
);
|
||||
|
||||
let storage_key: U256 = storage_key.into();
|
||||
|
||||
@@ -756,7 +756,10 @@ impl From<TransactionSource> for Transaction {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{eth::cache::EthStateCache, EthApi};
|
||||
use crate::{
|
||||
eth::{cache::EthStateCache, gas_oracle::GasPriceOracle},
|
||||
EthApi,
|
||||
};
|
||||
use reth_network_api::test_utils::NoopNetwork;
|
||||
use reth_primitives::{hex_literal::hex, Bytes};
|
||||
use reth_provider::test_utils::NoopProvider;
|
||||
@@ -769,11 +772,13 @@ mod tests {
|
||||
|
||||
let pool = testing_pool();
|
||||
|
||||
let cache = EthStateCache::spawn(noop_provider, Default::default());
|
||||
let eth_api = EthApi::new(
|
||||
noop_provider,
|
||||
pool.clone(),
|
||||
noop_network_provider,
|
||||
EthStateCache::spawn(NoopProvider::default(), Default::default()),
|
||||
cache.clone(),
|
||||
GasPriceOracle::new(noop_provider, Default::default(), cache),
|
||||
);
|
||||
|
||||
// https://etherscan.io/tx/0xa694b71e6c128a2ed8e2e0f6770bddbe52e3bb8f10e8472f9a79ab81497a8b5d
|
||||
|
||||
261
crates/rpc/rpc/src/eth/gas_oracle.rs
Normal file
261
crates/rpc/rpc/src/eth/gas_oracle.rs
Normal file
@@ -0,0 +1,261 @@
|
||||
//! An implementation of the eth gas price oracle, used for providing gas price estimates based on
|
||||
//! previous blocks.
|
||||
use crate::eth::{
|
||||
cache::EthStateCache,
|
||||
error::{EthApiError, EthResult, InvalidTransactionError},
|
||||
};
|
||||
use reth_primitives::{constants::GWEI_TO_WEI, BlockId, BlockNumberOrTag, H256, U256};
|
||||
use reth_provider::BlockProviderIdExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::warn;
|
||||
|
||||
/// The number of transactions sampled in a block
|
||||
pub const SAMPLE_NUMBER: u32 = 3;
|
||||
|
||||
/// The default maximum gas price to use for the estimate
|
||||
pub const DEFAULT_MAX_PRICE: U256 = U256::from_limbs([500_000_000_000u64, 0, 0, 0]);
|
||||
|
||||
/// The default minimum gas price, under which the sample will be ignored
|
||||
pub const DEFAULT_IGNORE_PRICE: U256 = U256::from_limbs([2u64, 0, 0, 0]);
|
||||
|
||||
/// Settings for the [GasPriceOracle]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GasPriceOracleConfig {
|
||||
/// The number of populated blocks to produce the gas price estimate
|
||||
pub blocks: u32,
|
||||
|
||||
/// The percentile of gas prices to use for the estimate
|
||||
pub percentile: u32,
|
||||
|
||||
/// The maximum number of headers to keep in the cache
|
||||
pub max_header_history: u64,
|
||||
|
||||
/// The maximum number of blocks for estimating gas price
|
||||
pub max_block_history: u64,
|
||||
|
||||
/// The default gas price to use if there are no blocks to use
|
||||
pub default: Option<U256>,
|
||||
|
||||
/// The maximum gas price to use for the estimate
|
||||
pub max_price: Option<U256>,
|
||||
|
||||
/// The minimum gas price, under which the sample will be ignored
|
||||
pub ignore_price: Option<U256>,
|
||||
}
|
||||
|
||||
impl Default for GasPriceOracleConfig {
|
||||
fn default() -> Self {
|
||||
GasPriceOracleConfig {
|
||||
blocks: 20,
|
||||
percentile: 60,
|
||||
max_header_history: 1024,
|
||||
max_block_history: 1024,
|
||||
default: None,
|
||||
max_price: Some(DEFAULT_MAX_PRICE),
|
||||
ignore_price: Some(DEFAULT_IGNORE_PRICE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates a gas price depending on recent blocks.
|
||||
#[derive(Debug)]
|
||||
pub struct GasPriceOracle<Client> {
|
||||
/// The type used to subscribe to block events and get block info
|
||||
client: Client,
|
||||
/// The cache for blocks
|
||||
cache: EthStateCache,
|
||||
/// The config for the oracle
|
||||
oracle_config: GasPriceOracleConfig,
|
||||
/// The latest calculated price and its block hash
|
||||
last_price: Mutex<GasPriceOracleResult>,
|
||||
}
|
||||
|
||||
impl<Client> GasPriceOracle<Client>
|
||||
where
|
||||
Client: BlockProviderIdExt + 'static,
|
||||
{
|
||||
/// Creates and returns the [GasPriceOracle].
|
||||
pub fn new(
|
||||
client: Client,
|
||||
mut oracle_config: GasPriceOracleConfig,
|
||||
cache: EthStateCache,
|
||||
) -> Self {
|
||||
// sanitize the perentile to be less than 100
|
||||
if oracle_config.percentile > 100 {
|
||||
warn!(prev_percentile=?oracle_config.percentile, "Invalid configured gas price percentile, using 100 instead");
|
||||
oracle_config.percentile = 100;
|
||||
}
|
||||
|
||||
Self { client, oracle_config, last_price: Default::default(), cache }
|
||||
}
|
||||
|
||||
/// Suggests a gas price estimate based on recent blocks, using the configured percentile.
|
||||
pub async fn suggest_tip_cap(&self) -> EthResult<U256> {
|
||||
let block = self
|
||||
.client
|
||||
.block_by_id(BlockId::Number(BlockNumberOrTag::Latest))?
|
||||
.ok_or(EthApiError::UnknownBlockNumber)?;
|
||||
|
||||
// seal for the block hash
|
||||
let header = block.header.seal_slow();
|
||||
|
||||
let mut last_price = self.last_price.lock().await;
|
||||
|
||||
// if we have stored a last price, then we check whether or not it was for the same head
|
||||
if last_price.block_hash == header.hash {
|
||||
return Ok(last_price.price)
|
||||
}
|
||||
|
||||
// if all responses are empty, then we can return a maximum of 2*check_block blocks' worth
|
||||
// of prices
|
||||
//
|
||||
// we only return more than check_block blocks' worth of prices if one or more return empty
|
||||
// transactions
|
||||
let mut current_hash = header.hash;
|
||||
let mut results = Vec::new();
|
||||
let mut populated_blocks = 0;
|
||||
|
||||
// we only check a maximum of 2 * max_block_history, or the number of blocks in the chain
|
||||
let max_blocks = if self.oracle_config.max_block_history * 2 > header.number {
|
||||
header.number
|
||||
} else {
|
||||
self.oracle_config.max_block_history * 2
|
||||
};
|
||||
|
||||
for _ in 0..max_blocks {
|
||||
let (parent_hash, block_values) = self
|
||||
.get_block_values(current_hash, SAMPLE_NUMBER as usize)
|
||||
.await?
|
||||
.ok_or(EthApiError::UnknownBlockNumber)?;
|
||||
|
||||
if block_values.is_empty() {
|
||||
results.push(U256::from(last_price.price));
|
||||
} else {
|
||||
results.extend(block_values);
|
||||
populated_blocks += 1;
|
||||
}
|
||||
|
||||
// break when we have enough populated blocks
|
||||
if populated_blocks >= self.oracle_config.blocks {
|
||||
break
|
||||
}
|
||||
|
||||
current_hash = parent_hash;
|
||||
}
|
||||
|
||||
// sort results then take the configured percentile result
|
||||
let mut price = last_price.price;
|
||||
if !results.is_empty() {
|
||||
results.sort_unstable();
|
||||
price = *results
|
||||
.get((results.len() - 1) * self.oracle_config.percentile as usize / 100)
|
||||
.expect("gas price index is a percent of nonzero array length, so a value always exists; qed");
|
||||
}
|
||||
|
||||
// constrain to the max price
|
||||
if let Some(max_price) = self.oracle_config.max_price {
|
||||
if price > max_price {
|
||||
price = max_price;
|
||||
}
|
||||
}
|
||||
|
||||
*last_price = GasPriceOracleResult { block_hash: header.hash, price };
|
||||
|
||||
Ok(price)
|
||||
}
|
||||
|
||||
/// Get the `limit` lowest effective tip values for the given block. If the oracle has a
|
||||
/// configured `ignore_price` threshold, then tip values under that threshold will be ignored
|
||||
/// before returning a result.
|
||||
///
|
||||
/// If the block cannot be found, then this will return `None`.
|
||||
///
|
||||
/// This method also returns the parent hash for the given block.
|
||||
async fn get_block_values(
|
||||
&self,
|
||||
block_hash: H256,
|
||||
limit: usize,
|
||||
) -> EthResult<Option<(H256, Vec<U256>)>> {
|
||||
// check the cache (this will hit the disk if the block is not cached)
|
||||
let block = match self.cache.get_block(block_hash).await? {
|
||||
Some(block) => block,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
// sort the transactions by effective tip
|
||||
// but first filter those that should be ignored
|
||||
let txs = block.body.iter();
|
||||
let mut txs = txs
|
||||
.filter(|tx| {
|
||||
if let Some(ignore_under) = self.oracle_config.ignore_price {
|
||||
if tx.effective_gas_tip(block.base_fee_per_gas).map(U256::from) <
|
||||
Some(ignore_under)
|
||||
{
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// recover sender, check if coinbase
|
||||
let sender = tx.recover_signer();
|
||||
match sender {
|
||||
// transactions will be filtered if this is false
|
||||
Some(addr) => addr != block.beneficiary,
|
||||
// TODO: figure out an error for this case or ignore
|
||||
None => false,
|
||||
}
|
||||
})
|
||||
// map all values to effective_gas_tip because we will be returning those values
|
||||
// anyways
|
||||
.map(|tx| tx.effective_gas_tip(block.base_fee_per_gas))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// now do the sort
|
||||
txs.sort_unstable();
|
||||
|
||||
// fill result with the top `limit` transactions
|
||||
let mut final_result = Vec::with_capacity(limit);
|
||||
for tx in txs.iter().take(limit) {
|
||||
// a `None` effective_gas_tip represents a transaction where the max_fee_per_gas is
|
||||
// less than the base fee
|
||||
let effective_tip = tx.ok_or(InvalidTransactionError::FeeCapTooLow)?;
|
||||
final_result.push(U256::from(effective_tip));
|
||||
}
|
||||
|
||||
Ok(Some((block.parent_hash, final_result)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the last result that the oracle returned
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GasPriceOracleResult {
|
||||
/// The block hash that the oracle used to calculate the price
|
||||
pub block_hash: H256,
|
||||
/// The price that the oracle calculated
|
||||
pub price: U256,
|
||||
}
|
||||
|
||||
impl Default for GasPriceOracleResult {
|
||||
fn default() -> Self {
|
||||
Self { block_hash: H256::zero(), price: U256::from(GWEI_TO_WEI) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use reth_primitives::constants::GWEI_TO_WEI;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn max_price_sanity() {
|
||||
assert_eq!(DEFAULT_MAX_PRICE, U256::from(500_000_000_000u64));
|
||||
assert_eq!(DEFAULT_MAX_PRICE, U256::from(500 * GWEI_TO_WEI))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignore_price_sanity() {
|
||||
assert_eq!(DEFAULT_IGNORE_PRICE, U256::from(2u64));
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ mod api;
|
||||
pub mod cache;
|
||||
pub mod error;
|
||||
mod filter;
|
||||
pub mod gas_oracle;
|
||||
mod id_provider;
|
||||
mod logs_utils;
|
||||
mod pubsub;
|
||||
|
||||
Reference in New Issue
Block a user