From f8d9483172cfb1d9bb9d8c6c6504c7adcd39030f Mon Sep 17 00:00:00 2001 From: Max Kalashnikoff Date: Mon, 30 Sep 2024 19:54:04 +0200 Subject: [PATCH] feat(providers): adding Berachain provider and Berachain bArtio support --- SUPPORTED_CHAINS.md | 1 + src/env/berachain.rs | 47 +++++++++++++++ src/env/mod.rs | 5 +- src/providers/berachain.rs | 96 ++++++++++++++++++++++++++++++ src/providers/mod.rs | 5 ++ tests/functional/http/berachain.rs | 18 ++++++ tests/functional/http/mod.rs | 1 + 7 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 src/env/berachain.rs create mode 100644 src/providers/berachain.rs create mode 100644 tests/functional/http/berachain.rs diff --git a/SUPPORTED_CHAINS.md b/SUPPORTED_CHAINS.md index 174ae5175..c7be52f2a 100644 --- a/SUPPORTED_CHAINS.md +++ b/SUPPORTED_CHAINS.md @@ -26,6 +26,7 @@ Chain name with associated `chainId` query param to use. | Avalanche C-Chain | eip155:43114 | | Linea [1](#footnote1) | eip155:59144 | | Polygon Amoy [1](#footnote1) | eip155:80002 | +| Berachain bArtio [1](#footnote1) | eip155:80084 | | Base Sepolia | eip155:84532 | | Arbitrum Sepolia | eip155:421614 | | Zora [1](#footnote1) | eip155:7777777 | diff --git a/src/env/berachain.rs b/src/env/berachain.rs new file mode 100644 index 000000000..829d65da2 --- /dev/null +++ b/src/env/berachain.rs @@ -0,0 +1,47 @@ +use { + super::ProviderConfig, + crate::providers::{Priority, Weight}, + std::collections::HashMap, +}; + +#[derive(Debug)] +pub struct BerachainConfig { + pub supported_chains: HashMap, +} + +impl Default for BerachainConfig { + fn default() -> Self { + Self { + supported_chains: default_supported_chains(), + } + } +} + +impl ProviderConfig for BerachainConfig { + fn supported_chains(self) -> HashMap { + self.supported_chains + } + + fn supported_ws_chains(self) -> HashMap { + HashMap::new() + } + + fn provider_kind(&self) -> crate::providers::ProviderKind { + crate::providers::ProviderKind::Berachain + } +} + +fn default_supported_chains() -> HashMap { + // Keep in-sync with SUPPORTED_CHAINS.md + + HashMap::from([ + // Berachain bArtio + ( + "eip155:80084".into(), + ( + "https://bartio.rpc.berachain.com/".into(), + Weight::new(Priority::Normal).unwrap(), + ), + ), + ]) +} diff --git a/src/env/mod.rs b/src/env/mod.rs index 603956f80..c12717183 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -14,11 +14,12 @@ use { std::{collections::HashMap, fmt::Display}, }; pub use { - aurora::*, base::*, binance::*, getblock::*, infura::*, mantle::*, near::*, pokt::*, - publicnode::*, quicknode::*, server::*, zksync::*, zora::*, + aurora::*, base::*, berachain::*, binance::*, getblock::*, infura::*, mantle::*, near::*, + pokt::*, publicnode::*, quicknode::*, server::*, zksync::*, zora::*, }; mod aurora; mod base; +mod berachain; mod binance; mod getblock; mod infura; diff --git a/src/providers/berachain.rs b/src/providers/berachain.rs new file mode 100644 index 000000000..7de617adb --- /dev/null +++ b/src/providers/berachain.rs @@ -0,0 +1,96 @@ +use { + super::{Provider, ProviderKind, RateLimited, RpcProvider, RpcProviderFactory}, + crate::{ + env::BerachainConfig, + error::{RpcError, RpcResult}, + }, + async_trait::async_trait, + axum::{ + http::HeaderValue, + response::{IntoResponse, Response}, + }, + hyper::{client::HttpConnector, http, Client, Method}, + hyper_tls::HttpsConnector, + std::collections::HashMap, + tracing::debug, +}; + +#[derive(Debug)] +pub struct BerachainProvider { + pub client: Client>, + pub supported_chains: HashMap, +} + +impl Provider for BerachainProvider { + fn supports_caip_chainid(&self, chain_id: &str) -> bool { + self.supported_chains.contains_key(chain_id) + } + + fn supported_caip_chains(&self) -> Vec { + self.supported_chains.keys().cloned().collect() + } + + fn provider_kind(&self) -> ProviderKind { + ProviderKind::Berachain + } +} + +#[async_trait] +impl RateLimited for BerachainProvider { + async fn is_rate_limited(&self, response: &mut Response) -> bool { + response.status() == http::StatusCode::TOO_MANY_REQUESTS + } +} + +#[async_trait] +impl RpcProvider for BerachainProvider { + #[tracing::instrument(skip(self, body), fields(provider = %self.provider_kind()), level = "debug")] + async fn proxy(&self, chain_id: &str, body: hyper::body::Bytes) -> RpcResult { + let uri = self + .supported_chains + .get(chain_id) + .ok_or(RpcError::ChainNotFound)?; + + let hyper_request = hyper::http::Request::builder() + .method(Method::POST) + .uri(uri) + .header("Content-Type", "application/json") + .body(hyper::body::Body::from(body))?; + + let response = self.client.request(hyper_request).await?; + let status = response.status(); + let body = hyper::body::to_bytes(response.into_body()).await?; + + if let Ok(response) = serde_json::from_slice::(&body) { + if response.error.is_some() && status.is_success() { + debug!( + "Strange: provider returned JSON RPC error, but status {status} is success: \ + Berachain: {response:?}" + ); + } + } + + let mut response = (status, body).into_response(); + response + .headers_mut() + .insert("Content-Type", HeaderValue::from_static("application/json")); + Ok(response) + } +} + +impl RpcProviderFactory for BerachainProvider { + #[tracing::instrument(level = "debug")] + fn new(provider_config: &BerachainConfig) -> Self { + let forward_proxy_client = Client::builder().build::<_, hyper::Body>(HttpsConnector::new()); + let supported_chains: HashMap = provider_config + .supported_chains + .iter() + .map(|(k, v)| (k.clone(), v.0.clone())) + .collect(); + + BerachainProvider { + client: forward_proxy_client, + supported_chains, + } + } +} diff --git a/src/providers/mod.rs b/src/providers/mod.rs index fb8ad4e96..c4d66fc90 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -43,6 +43,7 @@ use { mod aurora; mod base; +mod berachain; mod binance; mod coinbase; mod getblock; @@ -63,6 +64,7 @@ mod zora; pub use { aurora::AuroraProvider, base::BaseProvider, + berachain::BerachainProvider, binance::BinanceProvider, getblock::GetBlockProvider, infura::{InfuraProvider, InfuraWsProvider}, @@ -448,6 +450,7 @@ pub enum ProviderKind { Infura, Pokt, Binance, + Berachain, ZKSync, Publicnode, Base, @@ -472,6 +475,7 @@ impl Display for ProviderKind { ProviderKind::Infura => "Infura", ProviderKind::Pokt => "Pokt", ProviderKind::Binance => "Binance", + ProviderKind::Berachain => "Berachain", ProviderKind::ZKSync => "zkSync", ProviderKind::Publicnode => "Publicnode", ProviderKind::Base => "Base", @@ -497,6 +501,7 @@ impl ProviderKind { "Infura" => Some(Self::Infura), "Pokt" => Some(Self::Pokt), "Binance" => Some(Self::Binance), + "Berachain" => Some(Self::Berachain), "zkSync" => Some(Self::ZKSync), "Publicnode" => Some(Self::Publicnode), "Base" => Some(Self::Base), diff --git a/tests/functional/http/berachain.rs b/tests/functional/http/berachain.rs new file mode 100644 index 000000000..7585f7c77 --- /dev/null +++ b/tests/functional/http/berachain.rs @@ -0,0 +1,18 @@ +use { + super::check_if_rpc_is_responding_correctly_for_supported_chain, crate::context::ServerContext, + rpc_proxy::providers::ProviderKind, test_context::test_context, +}; + +#[test_context(ServerContext)] +#[tokio::test] +#[ignore] +async fn berachain_provider_eip155_80084(ctx: &mut ServerContext) { + // Berachain bArtio + check_if_rpc_is_responding_correctly_for_supported_chain( + ctx, + &ProviderKind::Berachain, + "eip155:80084", + "0x138d4", + ) + .await; +} diff --git a/tests/functional/http/mod.rs b/tests/functional/http/mod.rs index 7c159aeba..50e2d8516 100644 --- a/tests/functional/http/mod.rs +++ b/tests/functional/http/mod.rs @@ -8,6 +8,7 @@ use { pub(crate) mod aurora; pub(crate) mod base; +pub(crate) mod berachain; pub(crate) mod binance; pub(crate) mod getblock; pub(crate) mod infura;