Skip to content

Commit

Permalink
Use Polygon gas oracle (#2965)
Browse files Browse the repository at this point in the history
### Description

After investigating #2959, I found the following

this is a known problem with Polygon, explanation here
ethers-io/ethers.js#2828 (comment)

Fun fact Asa looked into this once
#771

Here's a discussion it in Foundry, which I found hoping that ethers-rs
folks had ran into this before
foundry-rs/foundry#1703

Foundry fixed this by using the Polygon gas station oracle which seems
to be recommended path
https://github.com/foundry-rs/foundry/pull/3368/files#diff-c89a4bbf7a90da118dcf00c5fe70eba78f8e5d95662bb5f039a353113e95042bR205

There's actually a polygon ethers middleware for this
https://docs.rs/ethers/latest/ethers/middleware/gas_oracle/polygon/struct.Polygon.html

So I (originally) borrowed this code from Foundry
https://github.com/foundry-rs/foundry/blob/master/crates/common/src/provider.rs#L254-L290

Changed to use Middlewares

This also means we can remove our existing janky Polygon logic

### Drive-by changes

<!--
Are there any minor or drive-by changes also included?
-->

### Related issues

<!--
- Fixes #[issue number here]
-->

### Backward compatibility

<!--
Are these changes backward compatible? Are there any infrastructure
implications, e.g. changes that would prohibit deploying older commits
using this infra tooling?

Yes/No
-->

### Testing

<!--
What kind of testing have these changes undergone?

None/Manual/Unit Tests
-->
  • Loading branch information
tkporter authored Nov 23, 2023
1 parent ece2be5 commit 1cebc39
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 32 deletions.
2 changes: 1 addition & 1 deletion rust/chains/hyperlane-ethereum/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ where
metadata.to_vec().into(),
RawHyperlaneMessage::from(message).to_vec().into(),
);
fill_tx_gas_params(tx, tx_gas_limit, self.provider.clone(), message.destination).await
fill_tx_gas_params(tx, tx_gas_limit, self.provider.clone()).await
}
}

Expand Down
70 changes: 54 additions & 16 deletions rust/chains/hyperlane-ethereum/src/trait_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use std::sync::Arc;
use std::time::Duration;

use async_trait::async_trait;
use ethers::middleware::gas_oracle::{
GasCategory, GasOracle, GasOracleMiddleware, Polygon, ProviderOracle,
};
use ethers::prelude::{
Http, JsonRpcClient, Middleware, NonceManagerMiddleware, Provider, Quorum, QuorumProvider,
SignerMiddleware, WeightedProvider, Ws, WsClientError,
Expand All @@ -17,7 +20,9 @@ use ethers_prometheus::json_rpc_client::{
use ethers_prometheus::middleware::{
MiddlewareMetrics, PrometheusMiddleware, PrometheusMiddlewareConf,
};
use hyperlane_core::{ChainCommunicationError, ChainResult, ContractLocator};
use hyperlane_core::{
ChainCommunicationError, ChainResult, ContractLocator, HyperlaneDomain, KnownHyperlaneDomain,
};

use crate::{signers::Signers, ConnectionConf, FallbackProvider, RetryingProvider};

Expand Down Expand Up @@ -89,7 +94,7 @@ pub trait BuildableWithProvider {
builder = builder.add_provider(weighted_provider);
}
let quorum_provider = builder.build();
self.wrap_with_metrics(quorum_provider, locator, signer, middleware_metrics)
self.build(quorum_provider, locator, signer, middleware_metrics)
.await?
}
ConnectionConf::HttpFallback { urls } => {
Expand All @@ -109,7 +114,7 @@ pub trait BuildableWithProvider {
builder = builder.add_provider(metrics_provider);
}
let fallback_provider = builder.build();
self.wrap_with_metrics(fallback_provider, locator, signer, middleware_metrics)
self.build(fallback_provider, locator, signer, middleware_metrics)
.await?
}
ConnectionConf::Http { url } => {
Expand All @@ -125,15 +130,14 @@ pub trait BuildableWithProvider {
&middleware_metrics,
);
let retrying_http_provider = RetryingProvider::new(metrics_provider, None, None);
self.wrap_with_metrics(retrying_http_provider, locator, signer, middleware_metrics)
self.build(retrying_http_provider, locator, signer, middleware_metrics)
.await?
}
ConnectionConf::Ws { url } => {
let ws = Ws::connect(url)
.await
.map_err(EthereumProviderConnectionError::from)?;
self.wrap_with_metrics(ws, locator, signer, middleware_metrics)
.await?
self.build(ws, locator, signer, middleware_metrics).await?
}
})
}
Expand Down Expand Up @@ -174,9 +178,9 @@ pub trait BuildableWithProvider {
)
}

/// Wrap the provider creation with metrics if provided; this is the second
/// step
async fn wrap_with_metrics<P>(
/// Create the provider, applying any middlewares (e.g. gas oracle, signer, metrics) as needed,
/// and then create the associated trait.
async fn build<P>(
&self,
client: P,
locator: &ContractLocator,
Expand All @@ -186,19 +190,20 @@ pub trait BuildableWithProvider {
where
P: JsonRpcClient + 'static,
{
let provider = Provider::new(client);
let provider = wrap_with_gas_oracle(Provider::new(client), locator.domain)?;

Ok(if let Some(metrics) = metrics {
let provider = Arc::new(PrometheusMiddleware::new(provider, metrics.0, metrics.1));
tokio::spawn(provider.start_updating_on_interval(METRICS_SCRAPE_INTERVAL));
self.wrap_with_signer(provider, locator, signer).await?
self.build_with_signer(provider, locator, signer).await?
} else {
self.wrap_with_signer(provider, locator, signer).await?
self.build_with_signer(provider, locator, signer).await?
})
}

/// Wrap the provider creation with a signing provider if signers were
/// provided; this is the third step.
async fn wrap_with_signer<M>(
/// provided, and then create the associated trait.
async fn build_with_signer<M>(
&self,
provider: M,
locator: &ContractLocator,
Expand All @@ -208,7 +213,7 @@ pub trait BuildableWithProvider {
M: Middleware + 'static,
{
Ok(if let Some(signer) = signer {
let signing_provider = build_signing_provider(provider, signer)
let signing_provider = wrap_with_signer(provider, signer)
.await
.map_err(ChainCommunicationError::from_other)?;
self.build_with_provider(signing_provider, locator)
Expand All @@ -224,7 +229,7 @@ pub trait BuildableWithProvider {
M: Middleware + 'static;
}

async fn build_signing_provider<M: Middleware>(
async fn wrap_with_signer<M: Middleware>(
provider: M,
signer: Signers,
) -> Result<SignerMiddleware<NonceManagerMiddleware<M>, Signers>, M::Error> {
Expand All @@ -237,3 +242,36 @@ async fn build_signing_provider<M: Middleware>(
let signing_provider = SignerMiddleware::new(provider, signer);
Ok(signing_provider)
}

fn build_polygon_gas_oracle(chain: ethers_core::types::Chain) -> ChainResult<Box<dyn GasOracle>> {
let gas_oracle = Polygon::new(chain)
.map_err(ChainCommunicationError::from_other)?
.category(GasCategory::Standard);
Ok(Box::new(gas_oracle) as Box<dyn GasOracle>)
}

/// Wrap the provider with a gas oracle middleware.
/// Polygon and Mumbai require using the Polygon gas oracle, see discussion here
/// https://github.com/foundry-rs/foundry/issues/1703.
/// Defaults to using the provider's gas oracle.
fn wrap_with_gas_oracle<M>(
provider: M,
domain: &HyperlaneDomain,
) -> ChainResult<GasOracleMiddleware<Arc<M>, Box<dyn GasOracle>>>
where
M: Middleware + 'static,
{
let provider = Arc::new(provider);
let gas_oracle: Box<dyn GasOracle> = {
match domain {
HyperlaneDomain::Known(KnownHyperlaneDomain::Polygon) => {
build_polygon_gas_oracle(ethers_core::types::Chain::Polygon)?
}
HyperlaneDomain::Known(KnownHyperlaneDomain::Mumbai) => {
build_polygon_gas_oracle(ethers_core::types::Chain::PolygonMumbai)?
}
_ => Box::new(ProviderOracle::new(provider.clone())),
}
};
Ok(GasOracleMiddleware::new(provider, gas_oracle))
}
15 changes: 1 addition & 14 deletions rust/chains/hyperlane-ethereum/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use ethers::{
};
use ethers_contract::builders::ContractCall;
use ethers_core::types::BlockNumber;
use hyperlane_core::{
utils::fmt_bytes, ChainCommunicationError, ChainResult, KnownHyperlaneDomain, H256, U256,
};
use hyperlane_core::{utils::fmt_bytes, ChainCommunicationError, ChainResult, H256, U256};
use tracing::{error, info};

use crate::Middleware;
Expand Down Expand Up @@ -73,7 +71,6 @@ pub(crate) async fn fill_tx_gas_params<M, D>(
tx: ContractCall<M, D>,
tx_gas_limit: Option<U256>,
provider: Arc<M>,
domain: u32,
) -> ChainResult<ContractCall<M, D>>
where
M: Middleware + 'static,
Expand All @@ -91,16 +88,6 @@ where
// Is not EIP 1559 chain
return Ok(tx.gas(gas_limit));
};
let max_priority_fee = if matches!(
KnownHyperlaneDomain::try_from(domain),
Ok(KnownHyperlaneDomain::Polygon)
) {
// Polygon needs a max priority fee >= 30 gwei
let min_polygon_fee = U256::from(30_000_000_000u64);
max_priority_fee.max(min_polygon_fee.into())
} else {
max_priority_fee
};
// Is EIP 1559 chain
let mut request = Eip1559TransactionRequest::new();
if let Some(from) = tx.tx.from() {
Expand Down
2 changes: 1 addition & 1 deletion rust/chains/hyperlane-ethereum/src/validator_announce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ where
announcement.value.storage_location,
serialized_signature.into(),
);
fill_tx_gas_params(tx, tx_gas_limit, self.provider.clone(), self.domain.id()).await
fill_tx_gas_params(tx, tx_gas_limit, self.provider.clone()).await
}
}

Expand Down

0 comments on commit 1cebc39

Please sign in to comment.