More gas oracles (#1251)
* Deriver auto_impls for GasOracle * Provider as GasOracle * impl fill_transaction in GasOracleMiddleware
This commit is contained in:
parent
0707270a05
commit
49b4ac7acb
|
@ -135,6 +135,18 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "auto_impl"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "auto_impl"
|
name = "auto_impl"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -1365,6 +1377,7 @@ name = "ethers-middleware"
|
||||||
version = "0.17.0"
|
version = "0.17.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"auto_impl 0.5.0",
|
||||||
"ethers-contract",
|
"ethers-contract",
|
||||||
"ethers-core",
|
"ethers-core",
|
||||||
"ethers-etherscan",
|
"ethers-etherscan",
|
||||||
|
@ -1393,7 +1406,7 @@ name = "ethers-providers"
|
||||||
version = "0.17.0"
|
version = "0.17.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"auto_impl",
|
"auto_impl 1.0.1",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
"ethers-core",
|
"ethers-core",
|
||||||
|
@ -1547,7 +1560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "089263294bb1c38ac73649a6ad563dd9a5142c8dc0482be15b8b9acb22a1611e"
|
checksum = "089263294bb1c38ac73649a6ad563dd9a5142c8dc0482be15b8b9acb22a1611e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.7.2",
|
"arrayvec 0.7.2",
|
||||||
"auto_impl",
|
"auto_impl 1.0.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"ethereum-types",
|
"ethereum-types",
|
||||||
"fastrlp-derive",
|
"fastrlp-derive",
|
||||||
|
|
|
@ -21,6 +21,7 @@ ethers-providers = { version = "^0.17.0", path = "../ethers-providers", default-
|
||||||
ethers-signers = { version = "^0.17.0", path = "../ethers-signers", default-features = false }
|
ethers-signers = { version = "^0.17.0", path = "../ethers-signers", default-features = false }
|
||||||
|
|
||||||
async-trait = { version = "0.1.50", default-features = false }
|
async-trait = { version = "0.1.50", default-features = false }
|
||||||
|
auto_impl = { version = "0.5.0", default-features = false }
|
||||||
serde = { version = "1.0.124", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.124", default-features = false, features = ["derive"] }
|
||||||
thiserror = { version = "1.0", default-features = false }
|
thiserror = { version = "1.0", default-features = false }
|
||||||
futures-util = { version = "^0.3" }
|
futures-util = { version = "^0.3" }
|
||||||
|
|
|
@ -23,7 +23,7 @@ struct GasNowResponseWrapper {
|
||||||
data: GasNowResponse,
|
data: GasNowResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct GasNowResponse {
|
pub struct GasNowResponse {
|
||||||
pub rapid: u64,
|
pub rapid: u64,
|
||||||
pub fast: u64,
|
pub fast: u64,
|
||||||
|
@ -41,7 +41,7 @@ impl GasNow {
|
||||||
pub fn with_client(client: Client) -> Self {
|
pub fn with_client(client: Client) -> Self {
|
||||||
let url = Url::parse(GAS_NOW_URL).expect("invalid url");
|
let url = Url::parse(GAS_NOW_URL).expect("invalid url");
|
||||||
|
|
||||||
Self { url, gas_category: GasCategory::Standard }
|
Self { client, url, gas_category: GasCategory::Standard }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the gas price category to be used when fetching the gas price.
|
/// Sets the gas price category to be used when fetching the gas price.
|
||||||
|
@ -64,7 +64,7 @@ impl GasNow {
|
||||||
|
|
||||||
impl Default for GasNow {
|
impl Default for GasNow {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(Client::new())
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,24 +56,11 @@ where
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_gas_price(&self) -> Result<U256, Self::Error> {
|
async fn fill_transaction(
|
||||||
Ok(self.gas_oracle.fetch().await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn estimate_eip1559_fees(
|
|
||||||
&self,
|
&self,
|
||||||
_: Option<fn(U256, Vec<Vec<U256>>) -> (U256, U256)>,
|
tx: &mut TypedTransaction,
|
||||||
) -> Result<(U256, U256), Self::Error> {
|
|
||||||
Ok(self.gas_oracle.estimate_eip1559_fees().await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>(
|
|
||||||
&self,
|
|
||||||
tx: T,
|
|
||||||
block: Option<BlockId>,
|
block: Option<BlockId>,
|
||||||
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let mut tx = tx.into();
|
|
||||||
|
|
||||||
match tx {
|
match tx {
|
||||||
TypedTransaction::Legacy(ref mut tx) => {
|
TypedTransaction::Legacy(ref mut tx) => {
|
||||||
if tx.gas_price.is_none() {
|
if tx.gas_price.is_none() {
|
||||||
|
@ -98,6 +85,28 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.inner().fill_transaction(tx, block).await.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_gas_price(&self) -> Result<U256, Self::Error> {
|
||||||
|
Ok(self.gas_oracle.fetch().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn estimate_eip1559_fees(
|
||||||
|
&self,
|
||||||
|
_: Option<fn(U256, Vec<Vec<U256>>) -> (U256, U256)>,
|
||||||
|
) -> Result<(U256, U256), Self::Error> {
|
||||||
|
Ok(self.gas_oracle.estimate_eip1559_fees().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>(
|
||||||
|
&self,
|
||||||
|
tx: T,
|
||||||
|
block: Option<BlockId>,
|
||||||
|
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
|
||||||
|
let mut tx = tx.into();
|
||||||
|
self.fill_transaction(&mut tx, block).await?;
|
||||||
self.inner.send_transaction(tx, block).await.map_err(MiddlewareError::MiddlewareError)
|
self.inner.send_transaction(tx, block).await.map_err(MiddlewareError::MiddlewareError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,18 @@ pub use cache::Cache;
|
||||||
mod polygon;
|
mod polygon;
|
||||||
pub use polygon::Polygon;
|
pub use polygon::Polygon;
|
||||||
|
|
||||||
|
mod gas_now;
|
||||||
|
pub use gas_now::GasNow;
|
||||||
|
|
||||||
|
mod provider_oracle;
|
||||||
|
pub use provider_oracle::ProviderOracle;
|
||||||
|
|
||||||
use ethers_core::types::U256;
|
use ethers_core::types::U256;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use auto_impl::auto_impl;
|
||||||
use reqwest::Error as ReqwestError;
|
use reqwest::Error as ReqwestError;
|
||||||
|
use std::error::Error;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const GWEI_TO_WEI: u64 = 1000000000;
|
const GWEI_TO_WEI: u64 = 1000000000;
|
||||||
|
@ -73,6 +81,10 @@ pub enum GasOracleError {
|
||||||
|
|
||||||
#[error("Chain is not supported by the oracle")]
|
#[error("Chain is not supported by the oracle")]
|
||||||
UnsupportedChain,
|
UnsupportedChain,
|
||||||
|
|
||||||
|
/// Error thrown when the provider failed.
|
||||||
|
#[error("Chain is not supported by the oracle")]
|
||||||
|
ProviderError(#[from] Box<dyn Error + Send + Sync>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `GasOracle` is a trait that an underlying gas oracle needs to implement.
|
/// `GasOracle` is a trait that an underlying gas oracle needs to implement.
|
||||||
|
@ -95,6 +107,7 @@ pub enum GasOracleError {
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
#[auto_impl(&, Box, Arc)]
|
||||||
pub trait GasOracle: Send + Sync + std::fmt::Debug {
|
pub trait GasOracle: Send + Sync + std::fmt::Debug {
|
||||||
/// Makes an asynchronous HTTP query to the underlying `GasOracle`
|
/// Makes an asynchronous HTTP query to the underlying `GasOracle`
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
use crate::gas_oracle::{GasOracle, GasOracleError};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use ethers_core::types::U256;
|
||||||
|
use ethers_providers::Middleware;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
/// Gas oracle from a [`Middleware`] implementation such as an
|
||||||
|
/// Ethereum RPC provider.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProviderOracle<M: Middleware> {
|
||||||
|
provider: M,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: Middleware> ProviderOracle<M> {
|
||||||
|
pub fn new(provider: M) -> Self {
|
||||||
|
Self { provider }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
impl<M: Middleware> GasOracle for ProviderOracle<M>
|
||||||
|
where
|
||||||
|
<M as Middleware>::Error: 'static,
|
||||||
|
{
|
||||||
|
async fn fetch(&self) -> Result<U256, GasOracleError> {
|
||||||
|
self.provider
|
||||||
|
.get_gas_price()
|
||||||
|
.await
|
||||||
|
.map_err(|err| GasOracleError::ProviderError(Box::new(err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> {
|
||||||
|
// TODO: Allow configuring different estimation functions.
|
||||||
|
self.provider
|
||||||
|
.estimate_eip1559_fees(None)
|
||||||
|
.await
|
||||||
|
.map_err(|err| GasOracleError::ProviderError(Box::new(err)))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue