Skip to content

Commit

Permalink
Adds fee estimation Config for EVM chains (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
haider-rs authored Jul 5, 2024
1 parent dcf1eb3 commit 901c7b1
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 30 deletions.
18 changes: 14 additions & 4 deletions chains/ethereum/server/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use crate::{
event_stream::EthereumEventStream,
log_filter::LogFilter,
proof::verify_proof,
utils::{AtBlockExt, EthereumRpcExt, PartialBlock},
utils::{
AtBlockExt, DefaultFeeEstimatorConfig, EthereumRpcExt, PartialBlock,
PolygonFeeEstimatorConfig,
},
};
use anyhow::{Context, Result};
use rosetta_config_ethereum::{
Expand Down Expand Up @@ -261,7 +264,11 @@ where
let address: H160 = address.address().parse()?;

let (max_fee_per_gas, max_priority_fee_per_gas) =
self.backend.estimate_eip1559_fees().await?;
if self.config().blockchain == "polygon" {
self.backend.estimate_eip1559_fees::<PolygonFeeEstimatorConfig>().await?
} else {
self.backend.estimate_eip1559_fees::<DefaultFeeEstimatorConfig>().await?
};
let tx = CallRequest {
from: Some(coinbase),
to: Some(address),
Expand Down Expand Up @@ -295,8 +302,11 @@ where
) -> Result<EthereumMetadata> {
let from: H160 = public_key.to_address(self.config().address_format).address().parse()?;
let to = options.destination.map(H160);
let (max_fee_per_gas, max_priority_fee_per_gas) =
self.backend.estimate_eip1559_fees().await?;
let (max_fee_per_gas, max_priority_fee_per_gas) = if self.config().blockchain == "polygon" {
self.backend.estimate_eip1559_fees::<PolygonFeeEstimatorConfig>().await?
} else {
self.backend.estimate_eip1559_fees::<DefaultFeeEstimatorConfig>().await?
};
let chain_id = self.backend.chain_id().await?;

let nonce = if let Some(nonce) = options.nonce {
Expand Down
90 changes: 64 additions & 26 deletions chains/ethereum/server/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,53 @@ impl AtBlockExt for AtBlock {
}
}

/// The number of blocks from the past for which the fee rewards are fetched for fee estimation.
const EIP1559_FEE_ESTIMATION_PAST_BLOCKS: u64 = 10;
/// The default percentile of gas premiums that are fetched for fee estimation.
const EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE: f64 = 5.0;
/// The default max priority fee per gas, used in case the base fee is within a threshold.
const EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE: u64 = 3_000_000_000;
/// The threshold for base fee below which we use the default priority fee, and beyond which we
/// estimate an appropriate value for priority fee.
const EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER: u64 = 100_000_000_000;
/// The threshold max change/difference (in %) at which we will ignore the fee history values
/// under it.
const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64 = 200;

fn estimate_priority_fee(rewards: &[Vec<U256>]) -> U256 {
pub trait FeeEstimatorConfig {
/// The number of blocks from the past for which the fee rewards are fetched for fee estimation.
const EIP1559_FEE_ESTIMATION_PAST_BLOCKS: u64;
/// The default percentile of gas premiums that are fetched for fee estimation.
const EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE: f64;
/// The default max priority fee per gas, used in case the base fee is within a threshold.
const EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE: u64;
/// The threshold for base fee below which we use the default priority fee, and beyond which we
/// estimate an appropriate value for priority fee.
const EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER: u64;
/// The threshold max change/difference (in %) at which we will ignore the fee history values
/// under it.
const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64;
/// Different evm blockchains returns base fee in different units. Like Ethereum returns in wei,
/// Polygon returns in gwei. so this multiplier converts them to wei format in order to
/// calculate gas fee
const EIP1559_BASE_FEE_MULTIPLIER: u64;
}

// Default config for ethereum and astar
pub struct DefaultFeeEstimatorConfig {}

impl FeeEstimatorConfig for DefaultFeeEstimatorConfig {
const EIP1559_FEE_ESTIMATION_PAST_BLOCKS: u64 = 10;
const EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE: f64 = 5.0;
const EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE: u64 = 3_000_000_000;
const EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER: u64 = 100_000_000_000;
const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64 = 200;
const EIP1559_BASE_FEE_MULTIPLIER: u64 = 1;
}

// Polygon Amoy fee estimator config
pub struct PolygonFeeEstimatorConfig {}

impl FeeEstimatorConfig for PolygonFeeEstimatorConfig {
// Computes safe low,
// reference: https://docs.polygon.technology/tools/gas/polygon-gas-station/
const EIP1559_FEE_ESTIMATION_PAST_BLOCKS: u64 = 15;
const EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE: f64 = 10.0;
const EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE: u64 = 30_000_000_000;
const EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER: u64 = 0;
const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64 = 200;
// Polygon returns base fee in gwei. we need to convert it into wei
const EIP1559_BASE_FEE_MULTIPLIER: u64 = 1_000_000_000;
}

fn estimate_priority_fee<F: FeeEstimatorConfig>(rewards: &[Vec<U256>]) -> U256 {
let mut rewards: Vec<U256> =
rewards.iter().map(|r| r[0]).filter(|r| *r > U256::zero()).collect();
if rewards.is_empty() {
Expand Down Expand Up @@ -97,7 +130,7 @@ fn estimate_priority_fee(rewards: &[Vec<U256>]) -> U256 {

// If we encountered a big change in fees at a certain position, then consider only
// the values >= it.
let values = if max_change >= EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE.into() &&
let values = if max_change >= F::EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE.into() &&
(max_change_index >= (rewards.len() / 2))
{
rewards[max_change_index..].to_vec()
Expand All @@ -121,14 +154,17 @@ fn base_fee_surged(base_fee_per_gas: U256) -> U256 {
}
}

pub fn eip1559_default_estimator(base_fee_per_gas: U256, rewards: &[Vec<U256>]) -> (U256, U256) {
pub fn eip1559_default_estimator<F: FeeEstimatorConfig>(
base_fee_per_gas: U256,
rewards: &[Vec<U256>],
) -> (U256, U256) {
let max_priority_fee_per_gas =
if base_fee_per_gas < U256::from(EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER) {
U256::from(EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE)
if base_fee_per_gas < U256::from(F::EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER) {
U256::from(F::EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE)
} else {
std::cmp::max(
estimate_priority_fee(rewards),
U256::from(EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE),
estimate_priority_fee::<F>(rewards),
U256::from(F::EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE),
)
};
let potential_max_fee = base_fee_surged(base_fee_per_gas);
Expand All @@ -153,7 +189,7 @@ pub trait EthereumRpcExt {
call_request: CallRequest,
) -> SubmitResult;

async fn estimate_eip1559_fees(&self) -> anyhow::Result<(U256, U256)>;
async fn estimate_eip1559_fees<F: FeeEstimatorConfig>(&self) -> anyhow::Result<(U256, U256)>;

async fn block_with_uncles(
&self,
Expand Down Expand Up @@ -257,25 +293,27 @@ where
}
}

async fn estimate_eip1559_fees(&self) -> anyhow::Result<(U256, U256)> {
async fn estimate_eip1559_fees<F: FeeEstimatorConfig>(&self) -> anyhow::Result<(U256, U256)> {
let Some(block) = self.block(AtBlock::Latest).await? else {
anyhow::bail!("latest block not found");
};
let Some(base_fee_per_gas) = block.header.base_fee_per_gas else {
let Some(mut base_fee_per_gas) = block.header.base_fee_per_gas else {
anyhow::bail!("EIP-1559 not activated");
};

base_fee_per_gas = base_fee_per_gas.saturating_mul(F::EIP1559_BASE_FEE_MULTIPLIER);

let fee_history = self
.fee_history(
EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
F::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
AtBlock::Latest,
&[EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
&[F::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
)
.await?;

// Estimate fees
let (max_fee_per_gas, max_priority_fee_per_gas) =
eip1559_default_estimator(base_fee_per_gas.into(), fee_history.reward.as_ref());
eip1559_default_estimator::<F>(base_fee_per_gas.into(), fee_history.reward.as_ref());
Ok((max_fee_per_gas, max_priority_fee_per_gas))
}

Expand Down

0 comments on commit 901c7b1

Please sign in to comment.