diff --git a/SUPPORTED_CHAINS.md b/SUPPORTED_CHAINS.md index adc6ba3a7c..0614113457 100644 --- a/SUPPORTED_CHAINS.md +++ b/SUPPORTED_CHAINS.md @@ -30,12 +30,14 @@ Chain name with associated `chainId` query param to use. | Ethereum Holesky | eip155:17000 | | Arbitrum | eip155:42161 | | Celo | eip155:42220 | +| Etherlink | eip155:42793 | | Avalanche Fuji Testnet [1](#footnote1) | eip155:43113 | | 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 | +| Etherlink Testnet | eip155:128123 | | Arbitrum Sepolia | eip155:421614 | | Scroll Mainnet [1](#footnote1) | eip155:534352 | | Scroll Sepolia Testnet [1](#footnote1) | eip155:534351 | diff --git a/src/env/etherlink.rs b/src/env/etherlink.rs new file mode 100644 index 0000000000..47b7d3f96a --- /dev/null +++ b/src/env/etherlink.rs @@ -0,0 +1,55 @@ +use { + super::ProviderConfig, + crate::providers::{Priority, Weight}, + std::collections::HashMap, +}; + +#[derive(Debug)] +pub struct EtherlinkConfig { + pub supported_chains: HashMap, +} + +impl Default for EtherlinkConfig { + fn default() -> Self { + Self { + supported_chains: default_supported_chains(), + } + } +} + +impl ProviderConfig for EtherlinkConfig { + 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::Etherlink + } +} + +fn default_supported_chains() -> HashMap { + // Keep in-sync with SUPPORTED_CHAINS.md + + HashMap::from([ + // Etherlink Mainnet + ( + "eip155:42793".into(), + ( + "https://node.mainnet.etherlink.com".into(), + Weight::new(Priority::Normal).unwrap(), + ), + ), + // Etherlink Testnet + ( + "eip155:128123".into(), + ( + "https://node.ghostnet.etherlink.com".into(), + Weight::new(Priority::Normal).unwrap(), + ), + ), + ]) +} \ No newline at end of file diff --git a/src/env/mod.rs b/src/env/mod.rs index 3a1422c31b..88765803cc 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -16,7 +16,7 @@ use { }; pub use { allnodes::*, arbitrum::*, aurora::*, base::*, berachain::*, binance::*, drpc::*, dune::*, - getblock::*, infura::*, lava::*, mantle::*, morph::*, near::*, odyssey::*, pokt::*, + etherlink::*, getblock::*, infura::*, lava::*, mantle::*, morph::*, near::*, odyssey::*, pokt::*, publicnode::*, quicknode::*, server::*, solscan::*, syndica::*, unichain::*, wemix::*, zerion::*, zksync::*, zora::*, }; @@ -28,6 +28,7 @@ mod berachain; mod binance; mod drpc; mod dune; +mod etherlink; mod getblock; mod infura; mod lava; diff --git a/src/lib.rs b/src/lib.rs index 614aba2068..d0e64cec33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ use { }, env::{ AllnodesConfig, ArbitrumConfig, AuroraConfig, BaseConfig, BerachainConfig, BinanceConfig, - DrpcConfig, DuneConfig, GetBlockConfig, InfuraConfig, LavaConfig, MantleConfig, + DrpcConfig, DuneConfig, EtherlinkConfig, GetBlockConfig, InfuraConfig, LavaConfig, MantleConfig, MorphConfig, NearConfig, OdysseyConfig, PoktConfig, PublicnodeConfig, QuicknodeConfig, SolScanConfig, SyndicaConfig, UnichainConfig, WemixConfig, ZKSyncConfig, ZerionConfig, ZoraConfig, @@ -33,7 +33,7 @@ use { hyper::{header::HeaderName, http, server::conn::AddrIncoming, Body, Server}, providers::{ AllnodesProvider, ArbitrumProvider, AuroraProvider, BaseProvider, BerachainProvider, - BinanceProvider, DrpcProvider, DuneProvider, GetBlockProvider, InfuraProvider, + BinanceProvider, DrpcProvider, DuneProvider, EtherlinkProvider, GetBlockProvider, InfuraProvider, InfuraWsProvider, LavaProvider, MantleProvider, MorphProvider, NearProvider, OdysseyProvider, PoktProvider, ProviderRepository, PublicnodeProvider, QuicknodeProvider, SolScanProvider, SyndicaProvider, UnichainProvider, WemixProvider, ZKSyncProvider, @@ -526,6 +526,7 @@ fn init_providers(config: &ProvidersConfig) -> ProviderRepository { providers.add_rpc_provider::(BaseConfig::default()); providers.add_rpc_provider::(BinanceConfig::default()); + providers.add_rpc_provider::(EtherlinkConfig::default()); providers.add_rpc_provider::(ZKSyncConfig::default()); providers.add_rpc_provider::(PublicnodeConfig::default()); providers.add_rpc_provider::(QuicknodeConfig::new( diff --git a/src/providers/etherlink.rs b/src/providers/etherlink.rs new file mode 100644 index 0000000000..b0d4afd706 --- /dev/null +++ b/src/providers/etherlink.rs @@ -0,0 +1,74 @@ +use { + super::{Provider, ProviderKind, RateLimited, RpcProvider, RpcProviderFactory}, + crate::{ + env::EtherlinkConfig, + error::{RpcError, RpcResult}, + }, + async_trait::async_trait, + axum::response::{IntoResponse, Response}, + hyper::{client::HttpConnector, http, Client, Method}, + hyper_tls::HttpsConnector, + std::collections::HashMap, + tracing::debug, +}; + +#[derive(Debug)] +pub struct EtherlinkProvider { + pub client: Client>, + pub supported_chains: HashMap, +} + +impl Provider for EtherlinkProvider { + 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::Etherlink + } +} + +#[async_trait] +impl RateLimited for EtherlinkProvider { + async fn is_rate_limited(&self, response: &mut Response) -> bool { + response.status() == http::StatusCode::TOO_MANY_REQUESTS + } +} + +#[async_trait] +impl RpcProvider for EtherlinkProvider { + 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?.into_response(); + } +} + +impl RpcProviderFactory for EtherlinkProvider { + fn new(provider_config: &EtherlinkConfig) -> 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(); + + EtherlinkProvider { + client: forward_proxy_client, + supported_chains, + } + } +} \ No newline at end of file diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 2b891bddc7..30d648b09c 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -71,6 +71,7 @@ mod bungee; mod coinbase; mod drpc; mod dune; +mod etherlink; mod getblock; mod infura; mod lava; @@ -105,6 +106,7 @@ pub use { bungee::BungeeProvider, drpc::DrpcProvider, dune::DuneProvider, + etherlink::EtherlinkProvider, getblock::GetBlockProvider, infura::{InfuraProvider, InfuraWsProvider}, lava::LavaProvider, @@ -660,6 +662,7 @@ pub enum ProviderKind { Binance, Berachain, Bungee, + Etherlink, ZKSync, Publicnode, Base, @@ -699,6 +702,7 @@ impl Display for ProviderKind { ProviderKind::Berachain => "Berachain", ProviderKind::Wemix => "Wemix", ProviderKind::Bungee => "Bungee", + ProviderKind::Etherlink => "Etherlink", ProviderKind::ZKSync => "zkSync", ProviderKind::Publicnode => "Publicnode", ProviderKind::Base => "Base", @@ -737,6 +741,7 @@ impl ProviderKind { "Binance" => Some(Self::Binance), "Berachain" => Some(Self::Berachain), "Bungee" => Some(Self::Bungee), + "Etherlink" => Some(Self::Etherlink), "zkSync" => Some(Self::ZKSync), "Publicnode" => Some(Self::Publicnode), "Base" => Some(Self::Base), diff --git a/tests/functional/http/etherlink.rs b/tests/functional/http/etherlink.rs new file mode 100644 index 0000000000..8e06f7305e --- /dev/null +++ b/tests/functional/http/etherlink.rs @@ -0,0 +1,28 @@ +use { + super::check_if_rpc_is_responding_correctly_for_supported_chain, + rpc_proxy::providers::ProviderKind, + crate::context::ServerContext, + test_context::test_context, +}; + +#[test_context(ServerContext)] +#[tokio::test] +#[ignore] +async fn etherlink_provider_eip155_42793(ctx: &mut ServerContext) { + // Etherlink Mainnet + check_if_rpc_is_responding_correctly_for_supported_chain( + ctx, + &ProviderKind::Etherlink, + "eip155:42793", + "0xa729", + ) + .await; + + check_if_rpc_is_responding_correctly_for_supported_chain( + ctx, + &ProviderKind::Etherlink, + "eip155:128123", + "0x1f47b", + ) + .await; +} \ No newline at end of file diff --git a/tests/functional/http/mod.rs b/tests/functional/http/mod.rs index e6f191bf80..c3a0d7bed5 100644 --- a/tests/functional/http/mod.rs +++ b/tests/functional/http/mod.rs @@ -11,6 +11,7 @@ pub(crate) mod aurora; pub(crate) mod base; pub(crate) mod berachain; pub(crate) mod binance; +pub(crate) mod etherlink; pub(crate) mod drpc; pub(crate) mod getblock; pub(crate) mod infura;