Skip to content
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

fix: lower priority fee #2148

Merged
merged 15 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 45 additions & 18 deletions hotshot-state-prover/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ use async_std::{
};
use contract_bindings::light_client::{LightClient, LightClientErrors};
use displaydoc::Display;
use ethers::middleware::{
gas_oracle::{GasCategory, GasOracle},
signer::SignerMiddlewareError,
};
use ethers::{
core::k256::ecdsa::SigningKey,
middleware::SignerMiddleware,
providers::{Http, Middleware, Provider, ProviderError},
signers::{LocalWallet, Signer, Wallet},
types::{Address, U256},
types::{transaction::eip2718::TypedTransaction, Address, U256},
};
use futures::FutureExt;
use hotshot_contract_adapter::{
Expand All @@ -42,6 +46,7 @@ use jf_pcs::prelude::UnivariateUniversalParams;
use jf_plonk::errors::PlonkError;
use jf_relation::Circuit as _;
use jf_signature::constants::CS_ID_SCHNORR;
use sequencer_utils::blocknative::BlockNative;
use sequencer_utils::deployer::is_proxy_contract;
use serde::Deserialize;
use surf_disco::Client;
Expand Down Expand Up @@ -276,11 +281,8 @@ async fn prepare_contract(

/// get the `finalizedState` from the LightClient contract storage on L1
pub async fn read_contract_state(
provider: Url,
key: SigningKey,
light_client_address: Address,
contract: &LightClient<SignerWallet>,
) -> Result<(LightClientState, StakeTableState), ProverError> {
let contract = prepare_contract(provider, key, light_client_address).await?;
let state: ParsedLightClientState = match contract.finalized_state().call().await {
Ok(s) => s.into(),
Err(e) => {
Expand All @@ -306,16 +308,34 @@ pub async fn read_contract_state(
pub async fn submit_state_and_proof(
proof: Proof,
public_input: PublicInput,
provider: Url,
key: SigningKey,
light_client_address: Address,
contract: &LightClient<SignerWallet>,
) -> Result<(), ProverError> {
let contract = prepare_contract(provider, key, light_client_address).await?;

// prepare the input the contract call and the tx itself
let proof: ParsedPlonkProof = proof.into();
let new_state: ParsedLightClientState = public_input.into();
let tx = contract.new_finalized_state(new_state.into(), proof.into());

let mut tx = contract.new_finalized_state(new_state.into(), proof.into());

// only use gas oracle for mainnet
if contract.client_ref().get_chainid().await?.as_u64() == 1 {
alxiong marked this conversation as resolved.
Show resolved Hide resolved
let gas_oracle = BlockNative::new(None).category(GasCategory::SafeLow);
match gas_oracle.estimate_eip1559_fees().await {
Ok((max_fee, priority_fee)) => {
if let TypedTransaction::Eip1559(inner) = &mut tx.tx {
inner.max_fee_per_gas = Some(max_fee);
inner.max_priority_fee_per_gas = Some(priority_fee);
tracing::info!(
"Setting maxFeePerGas: {}; maxPriorityFeePerGas to: {}",
max_fee,
priority_fee
);
}
}
Err(e) => {
tracing::warn!("!! BlockNative Price Oracle failed: {}", e);
alysiahuggins marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

// send the tx
let (receipt, included_block) = sequencer_utils::contract_send::<_, _, LightClientErrors>(&tx)
Expand Down Expand Up @@ -349,8 +369,8 @@ pub async fn sync_state<ApiVer: StaticVersionType>(
let bundle = fetch_latest_state(relay_server_client).await?;
tracing::info!("Bundle accumulated weight: {}", bundle.accumulated_weight);
tracing::info!("Latest HotShot block height: {}", bundle.state.block_height);
let (old_state, st_state) =
read_contract_state(provider.clone(), key.clone(), light_client_address).await?;
let contract = prepare_contract(provider.clone(), key.clone(), light_client_address).await?;
let (old_state, st_state) = read_contract_state(&contract).await?;
tracing::info!(
"Current HotShot block height on contract: {}",
old_state.block_height
Expand Down Expand Up @@ -408,7 +428,7 @@ pub async fn sync_state<ApiVer: StaticVersionType>(
let proof_gen_elapsed = Instant::now().signed_duration_since(proof_gen_start);
tracing::info!("Proof generation completed. Elapsed: {proof_gen_elapsed:.3}");

submit_state_and_proof(proof, public_input, provider, key, light_client_address).await?;
submit_state_and_proof(proof, public_input, &contract).await?;

tracing::info!("Successfully synced light client state.");
Ok(())
Expand Down Expand Up @@ -521,6 +541,8 @@ pub enum ProverError {
PlonkError(PlonkError),
/// Internal error
Internal(String),
/// General network issue: {0}
NetworkError(anyhow::Error),
}

impl From<ServerError> for ProverError {
Expand All @@ -546,6 +568,11 @@ impl From<ProviderError> for ProverError {
Self::ContractError(anyhow!("{}", err))
}
}
impl From<SignerMiddlewareError<Provider<Http>, LocalWallet>> for ProverError {
fn from(err: SignerMiddlewareError<Provider<Http>, LocalWallet>) -> Self {
Self::ContractError(anyhow!("{}", err))
}
}

impl std::error::Error for ProverError {}

Expand Down Expand Up @@ -843,12 +870,13 @@ mod test {

let mut config = StateProverConfig::default();
config.update_l1_info(&anvil, contract.address());
let (state, st_state) = super::read_contract_state(
let contract = super::prepare_contract(
config.provider,
config.signing_key,
config.light_client_address,
)
.await?;
let (state, st_state) = super::read_contract_state(&contract).await?;

assert_eq!(state, genesis.into());
assert_eq!(st_state, stake_genesis.into());
Expand Down Expand Up @@ -879,14 +907,13 @@ mod test {
let (pi, proof) = gen_state_proof(new_state.clone(), &stake_genesis, &state_keys, &st);
tracing::info!("Successfully generated proof for new state.");

super::submit_state_and_proof(
proof,
pi,
let contract = super::prepare_contract(
config.provider,
config.signing_key,
config.light_client_address,
)
.await?;
super::submit_state_and_proof(proof, pi, &contract).await?;
tracing::info!("Successfully submitted new finalized state to L1.");
// test if new state is updated in l1
let finalized_l1: ParsedLightClientState = contract.finalized_state().await?.into();
Expand Down
3 changes: 3 additions & 0 deletions marketplace-builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,7 @@ url = { workspace = true }
vbs = { workspace = true }

[dev-dependencies]
hotshot-query-service = { workspace = true }
sequencer = { path = "../sequencer", features = ["testing"] }
sequencer-utils = { path = "../utils", features = ["testing"] }
tempfile = { workspace = true }
3 changes: 3 additions & 0 deletions utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ anyhow = { workspace = true }
ark-serialize = { workspace = true, features = ["derive"] }
async-compatibility-layer = { workspace = true }
async-std = { workspace = true }
async-trait = { workspace = true }
clap = { workspace = true }
committable = "0.2"
contract-bindings = { path = "../contract-bindings" }
Expand All @@ -22,6 +23,8 @@ futures = { workspace = true }
hotshot-contract-adapter = { workspace = true }
log-panics = { workspace = true }
portpicker = { workspace = true }
# for price oracle and align with ethers-rs dep
reqwest = { version = "0.11.14", default-features = false, features = ["json", "rustls-tls"] }
serde = { workspace = true }
serde_json = "^1.0.113"
surf = "2.3.2"
Expand Down
150 changes: 150 additions & 0 deletions utils/src/blocknative.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//! Copy from <https://github.com/gakonst/ethers-rs/blob/master/ethers-middleware/src/gas_oracle/blocknative.rs>
//! which is unmaintained and out-of-sync with the latest blocknative feed
//!
//! TDOO: revisit this or remove this when switching to `alloy-rs`
use async_trait::async_trait;
use ethers::{
middleware::gas_oracle::{from_gwei_f64, GasCategory, GasOracle, GasOracleError, Result},
types::U256,
};
use reqwest::{header::AUTHORIZATION, Client};
use serde::Deserialize;
use std::collections::HashMap;
use url::Url;

const URL: &str = "https://api.blocknative.com/gasprices/blockprices";
alysiahuggins marked this conversation as resolved.
Show resolved Hide resolved

/// A client over HTTP for the [BlockNative](https://www.blocknative.com/gas-estimator) gas tracker API
/// that implements the `GasOracle` trait.
#[derive(Clone, Debug)]
#[must_use]
pub struct BlockNative {
client: Client,
url: Url,
api_key: Option<String>,
gas_category: GasCategory,
}

#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Response {
pub system: String,
pub network: String,
pub unit: String,
pub max_price: u64,
pub block_prices: Vec<BlockPrice>,
pub estimated_base_fees: Option<Vec<HashMap<String, Vec<BaseFeeEstimate>>>>,
}

#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct BlockPrice {
pub block_number: u64,
pub estimated_transaction_count: u64,
pub base_fee_per_gas: f64,
pub estimated_prices: Vec<GasEstimate>,
}

#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct GasEstimate {
pub confidence: u64,
pub price: f64,
pub max_priority_fee_per_gas: f64,
pub max_fee_per_gas: f64,
}

#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct BaseFeeEstimate {
pub confidence: u64,
pub base_fee: f64,
}

impl Response {
#[inline]
pub fn estimate_from_category(&self, gas_category: &GasCategory) -> Result<GasEstimate> {
let confidence = gas_category_to_confidence(gas_category);
let price = self
.block_prices
.first()
.ok_or(GasOracleError::InvalidResponse)?
.estimated_prices
.iter()
.find(|p| p.confidence == confidence)
.ok_or(GasOracleError::GasCategoryNotSupported)?;
Ok(*price)
}
}

impl Default for BlockNative {
fn default() -> Self {
Self::new(None)
}
}

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GasOracle for BlockNative {
async fn fetch(&self) -> Result<U256> {
let estimate = self
.query()
.await?
.estimate_from_category(&self.gas_category)?;
Ok(from_gwei_f64(estimate.price))
}

async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> {
let estimate = self
.query()
.await?
.estimate_from_category(&self.gas_category)?;
let max = from_gwei_f64(estimate.max_fee_per_gas);
let prio = from_gwei_f64(estimate.max_priority_fee_per_gas);
Ok((max, prio))
}
}

impl BlockNative {
/// Creates a new [BlockNative](https://www.blocknative.com/gas-estimator) gas oracle.
pub fn new(api_key: Option<String>) -> Self {
Self::with_client(Client::new(), api_key)
}

/// Same as [`Self::new`] but with a custom [`Client`].
pub fn with_client(client: Client, api_key: Option<String>) -> Self {
let url = Url::parse(URL).unwrap();
Self {
client,
api_key,
url,
gas_category: GasCategory::Standard,
}
}

/// Sets the gas price category to be used when fetching the gas price.
pub fn category(mut self, gas_category: GasCategory) -> Self {
self.gas_category = gas_category;
self
}

/// Perform a request to the gas price API and deserialize the response.
pub async fn query(&self) -> Result<Response, GasOracleError> {
let mut request = self.client.get(self.url.clone());
if let Some(api_key) = self.api_key.as_ref() {
request = request.header(AUTHORIZATION, api_key);
}
let response = request.send().await?.error_for_status()?.json().await?;
Ok(response)
}
}

#[inline]
fn gas_category_to_confidence(gas_category: &GasCategory) -> u64 {
match gas_category {
GasCategory::SafeLow => 80,
GasCategory::Standard => 90,
GasCategory::Fast => 95,
GasCategory::Fastest => 99,
}
}
1 change: 1 addition & 0 deletions utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use ethers::{
use tempfile::TempDir;
use url::Url;

pub mod blocknative;
pub mod deployer;
pub mod logging;
pub mod ser;
Expand Down