From 1ec1adf73aa597e78c00bed98cd519320ec1da38 Mon Sep 17 00:00:00 2001 From: Max Kalashnikoff Date: Mon, 15 Jan 2024 12:48:03 +0100 Subject: [PATCH] feat(providers): adding the Aurora native mainnet and testnet RPC --- src/env/aurora.rs | 55 ++++++++++++++++++ src/env/mod.rs | 2 + src/providers/aurora.rs | 99 +++++++++++++++++++++++++++++++++ src/providers/mod.rs | 4 ++ tests/functional/http/aurora.rs | 22 ++++++++ tests/functional/http/mod.rs | 1 + 6 files changed, 183 insertions(+) create mode 100644 src/env/aurora.rs create mode 100644 src/providers/aurora.rs create mode 100644 tests/functional/http/aurora.rs diff --git a/src/env/aurora.rs b/src/env/aurora.rs new file mode 100644 index 000000000..8b4f5950a --- /dev/null +++ b/src/env/aurora.rs @@ -0,0 +1,55 @@ +use { + super::ProviderConfig, + crate::providers::{Priority, Weight}, + std::collections::HashMap, +}; + +#[derive(Debug)] +pub struct AuroraConfig { + pub supported_chains: HashMap, +} + +impl Default for AuroraConfig { + fn default() -> Self { + Self { + supported_chains: default_supported_chains(), + } + } +} + +impl ProviderConfig for AuroraConfig { + 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::Aurora + } +} + +fn default_supported_chains() -> HashMap { + // Keep in-sync with SUPPORTED_CHAINS.md + + HashMap::from([ + // Aurora Mainnet + ( + "eip155:1313161554".into(), + ( + "https://mainnet.aurora.dev".into(), + Weight::new(Priority::High).unwrap(), + ), + ), + // Aurora Testnet + ( + "eip155:1313161555".into(), + ( + "https://testnet.aurora.dev".into(), + Weight::new(Priority::High).unwrap(), + ), + ), + ]) +} diff --git a/src/env/mod.rs b/src/env/mod.rs index 2525e41d6..d7b9c30c1 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -11,6 +11,7 @@ use { std::{collections::HashMap, fmt::Display}, }; pub use { + aurora::*, base::*, binance::*, infura::*, @@ -21,6 +22,7 @@ pub use { zksync::*, zora::*, }; +mod aurora; mod base; mod binance; mod infura; diff --git a/src/providers/aurora.rs b/src/providers/aurora.rs new file mode 100644 index 000000000..ecfce0352 --- /dev/null +++ b/src/providers/aurora.rs @@ -0,0 +1,99 @@ +use { + super::{Provider, ProviderKind, RateLimited, RpcProvider, RpcProviderFactory}, + crate::{ + env::AuroraConfig, + 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::info, +}; + +#[derive(Debug)] +pub struct AuroraProvider { + pub client: Client>, + pub supported_chains: HashMap, +} + +impl Provider for AuroraProvider { + 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::Aurora + } +} + +#[async_trait] +impl RateLimited for AuroraProvider { + async fn is_rate_limited(&self, response: &mut Response) -> bool + where + Self: Sized, + { + response.status() == http::StatusCode::TOO_MANY_REQUESTS + } +} + +#[async_trait] +impl RpcProvider for AuroraProvider { + #[tracing::instrument(skip(self, body), fields(provider = %self.provider_kind()))] + 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() { + info!( + "Strange: provider returned JSON RPC error, but status {status} is success: \ + Aurora: {response:?}" + ); + } + } + + let mut response = (status, body).into_response(); + response + .headers_mut() + .insert("Content-Type", HeaderValue::from_static("application/json")); + Ok(response) + } +} + +impl RpcProviderFactory for AuroraProvider { + #[tracing::instrument] + fn new(provider_config: &AuroraConfig) -> 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(); + + AuroraProvider { + client: forward_proxy_client, + supported_chains, + } + } +} diff --git a/src/providers/mod.rs b/src/providers/mod.rs index a9918969f..a474790e0 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -24,6 +24,7 @@ use { wc::metrics::TaskMetrics, }; +mod aurora; mod base; mod binance; mod coinbase; @@ -270,6 +271,7 @@ impl ProviderRepository { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ProviderKind { + Aurora, Infura, Pokt, Binance, @@ -285,6 +287,7 @@ pub enum ProviderKind { impl Display for ProviderKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", match self { + ProviderKind::Aurora => "Aurora", ProviderKind::Infura => "Infura", ProviderKind::Pokt => "Pokt", ProviderKind::Binance => "Binance", @@ -302,6 +305,7 @@ impl Display for ProviderKind { impl ProviderKind { pub fn from_str(s: &str) -> Option { match s { + "Aurora" => Some(Self::Aurora), "Infura" => Some(Self::Infura), "Pokt" => Some(Self::Pokt), "Binance" => Some(Self::Binance), diff --git a/tests/functional/http/aurora.rs b/tests/functional/http/aurora.rs new file mode 100644 index 000000000..17f5190d3 --- /dev/null +++ b/tests/functional/http/aurora.rs @@ -0,0 +1,22 @@ +use { + super::check_if_rpc_is_responding_correctly_for_supported_chain, + crate::context::ServerContext, + test_context::test_context, +}; + +#[test_context(ServerContext)] +#[tokio::test] +#[ignore] +async fn aurora_provider(ctx: &mut ServerContext) { + // Aurora Mainnet + check_if_rpc_is_responding_correctly_for_supported_chain( + ctx, + "eip155:1313161554", + "0x4e454152", + ) + .await; + + // Aurora Testnet + check_if_rpc_is_responding_correctly_for_supported_chain(ctx, "eip155:1313161555", "0x4e454153") + .await +} diff --git a/tests/functional/http/mod.rs b/tests/functional/http/mod.rs index f796fe6d3..3d178ec2c 100644 --- a/tests/functional/http/mod.rs +++ b/tests/functional/http/mod.rs @@ -6,6 +6,7 @@ use { test_context::test_context, }; +pub(crate) mod aurora; pub(crate) mod base; pub(crate) mod binance; pub(crate) mod infura;