Skip to content

Commit ad1ab59

Browse files
authored
feat(providers): adding dRPC endpoint for Solana (#902)
1 parent d4948b3 commit ad1ab59

File tree

7 files changed

+182
-11
lines changed

7 files changed

+182
-11
lines changed

src/env/drpc.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use {
2+
super::ProviderConfig,
3+
crate::providers::{Priority, Weight},
4+
std::collections::HashMap,
5+
};
6+
7+
#[derive(Debug)]
8+
pub struct DrpcConfig {
9+
pub supported_chains: HashMap<String, (String, Weight)>,
10+
}
11+
12+
impl Default for DrpcConfig {
13+
fn default() -> Self {
14+
Self {
15+
supported_chains: default_supported_chains(),
16+
}
17+
}
18+
}
19+
20+
impl ProviderConfig for DrpcConfig {
21+
fn supported_chains(self) -> HashMap<String, (String, Weight)> {
22+
self.supported_chains
23+
}
24+
25+
fn supported_ws_chains(self) -> HashMap<String, (String, Weight)> {
26+
HashMap::new()
27+
}
28+
29+
fn provider_kind(&self) -> crate::providers::ProviderKind {
30+
crate::providers::ProviderKind::Drpc
31+
}
32+
}
33+
34+
fn default_supported_chains() -> HashMap<String, (String, Weight)> {
35+
// Keep in-sync with SUPPORTED_CHAINS.md
36+
37+
HashMap::from([
38+
// Solana Mainnet
39+
(
40+
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp".into(),
41+
(
42+
"https://solana.drpc.org/".into(),
43+
Weight::new(Priority::Normal).unwrap(),
44+
),
45+
),
46+
])
47+
}

src/env/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,16 @@ use {
1414
std::{collections::HashMap, fmt::Display},
1515
};
1616
pub use {
17-
arbitrum::*, aurora::*, base::*, berachain::*, binance::*, dune::*, getblock::*, infura::*,
18-
lava::*, mantle::*, morph::*, near::*, pokt::*, publicnode::*, quicknode::*, server::*,
19-
solscan::*, unichain::*, wemix::*, zerion::*, zksync::*, zora::*,
17+
arbitrum::*, aurora::*, base::*, berachain::*, binance::*, drpc::*, dune::*, getblock::*,
18+
infura::*, lava::*, mantle::*, morph::*, near::*, pokt::*, publicnode::*, quicknode::*,
19+
server::*, solscan::*, unichain::*, wemix::*, zerion::*, zksync::*, zora::*,
2020
};
2121
mod arbitrum;
2222
mod aurora;
2323
mod base;
2424
mod berachain;
2525
mod binance;
26+
mod drpc;
2627
mod dune;
2728
mod getblock;
2829
mod infura;

src/lib.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,21 @@ use {
1818
Router,
1919
},
2020
env::{
21-
ArbitrumConfig, AuroraConfig, BaseConfig, BerachainConfig, BinanceConfig, DuneConfig,
22-
GetBlockConfig, InfuraConfig, LavaConfig, MantleConfig, MorphConfig, NearConfig,
23-
PoktConfig, PublicnodeConfig, QuicknodeConfig, SolScanConfig, UnichainConfig, WemixConfig,
24-
ZKSyncConfig, ZerionConfig, ZoraConfig,
21+
ArbitrumConfig, AuroraConfig, BaseConfig, BerachainConfig, BinanceConfig, DrpcConfig,
22+
DuneConfig, GetBlockConfig, InfuraConfig, LavaConfig, MantleConfig, MorphConfig,
23+
NearConfig, PoktConfig, PublicnodeConfig, QuicknodeConfig, SolScanConfig, UnichainConfig,
24+
WemixConfig, ZKSyncConfig, ZerionConfig, ZoraConfig,
2525
},
2626
error::RpcResult,
2727
http::Request,
2828
hyper::{header::HeaderName, http, server::conn::AddrIncoming, Body, Server},
2929
providers::{
3030
ArbitrumProvider, AuroraProvider, BaseProvider, BerachainProvider, BinanceProvider,
31-
DuneProvider, GetBlockProvider, InfuraProvider, InfuraWsProvider, LavaProvider,
32-
MantleProvider, MorphProvider, NearProvider, PoktProvider, ProviderRepository,
33-
PublicnodeProvider, QuicknodeProvider, SolScanProvider, UnichainProvider, WemixProvider,
34-
ZKSyncProvider, ZerionProvider, ZoraProvider, ZoraWsProvider,
31+
DrpcProvider, DuneProvider, GetBlockProvider, InfuraProvider, InfuraWsProvider,
32+
LavaProvider, MantleProvider, MorphProvider, NearProvider, PoktProvider,
33+
ProviderRepository, PublicnodeProvider, QuicknodeProvider, SolScanProvider,
34+
UnichainProvider, WemixProvider, ZKSyncProvider, ZerionProvider, ZoraProvider,
35+
ZoraWsProvider,
3536
},
3637
sqlx::postgres::PgPoolOptions,
3738
std::{
@@ -512,6 +513,7 @@ fn init_providers(config: &ProvidersConfig) -> ProviderRepository {
512513
.add_rpc_provider::<LavaProvider, LavaConfig>(LavaConfig::new(config.lava_api_key.clone()));
513514
providers.add_rpc_provider::<MorphProvider, MorphConfig>(MorphConfig::default());
514515
providers.add_rpc_provider::<WemixProvider, WemixConfig>(WemixConfig::default());
516+
providers.add_rpc_provider::<DrpcProvider, DrpcConfig>(DrpcConfig::default());
515517

516518
if let Some(getblock_access_tokens) = &config.getblock_access_tokens {
517519
providers.add_rpc_provider::<GetBlockProvider, GetBlockConfig>(GetBlockConfig::new(

src/providers/drpc.rs

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use {
2+
super::{Provider, ProviderKind, RateLimited, RpcProvider, RpcProviderFactory},
3+
crate::{
4+
env::DrpcConfig,
5+
error::{RpcError, RpcResult},
6+
},
7+
async_trait::async_trait,
8+
axum::{
9+
http::HeaderValue,
10+
response::{IntoResponse, Response},
11+
},
12+
hyper::{client::HttpConnector, http, Client, Method},
13+
hyper_tls::HttpsConnector,
14+
std::collections::HashMap,
15+
tracing::debug,
16+
};
17+
18+
#[derive(Debug)]
19+
pub struct DrpcProvider {
20+
pub client: Client<HttpsConnector<HttpConnector>>,
21+
pub supported_chains: HashMap<String, String>,
22+
}
23+
24+
impl Provider for DrpcProvider {
25+
fn supports_caip_chainid(&self, chain_id: &str) -> bool {
26+
self.supported_chains.contains_key(chain_id)
27+
}
28+
29+
fn supported_caip_chains(&self) -> Vec<String> {
30+
self.supported_chains.keys().cloned().collect()
31+
}
32+
33+
fn provider_kind(&self) -> ProviderKind {
34+
ProviderKind::Drpc
35+
}
36+
}
37+
38+
#[async_trait]
39+
impl RateLimited for DrpcProvider {
40+
async fn is_rate_limited(&self, response: &mut Response) -> bool {
41+
response.status() == http::StatusCode::TOO_MANY_REQUESTS
42+
}
43+
}
44+
45+
#[async_trait]
46+
impl RpcProvider for DrpcProvider {
47+
#[tracing::instrument(skip(self, body), fields(provider = %self.provider_kind()), level = "debug")]
48+
async fn proxy(&self, chain_id: &str, body: hyper::body::Bytes) -> RpcResult<Response> {
49+
let uri = self
50+
.supported_chains
51+
.get(chain_id)
52+
.ok_or(RpcError::ChainNotFound)?;
53+
54+
let hyper_request = hyper::http::Request::builder()
55+
.method(Method::POST)
56+
.uri(uri)
57+
.header("Content-Type", "application/json")
58+
.body(hyper::body::Body::from(body))?;
59+
60+
let response = self.client.request(hyper_request).await?;
61+
let status = response.status();
62+
let body = hyper::body::to_bytes(response.into_body()).await?;
63+
64+
if let Ok(response) = serde_json::from_slice::<jsonrpc::Response>(&body) {
65+
if response.error.is_some() && status.is_success() {
66+
debug!(
67+
"Strange: provider returned JSON RPC error, but status {status} is success: \
68+
dRPC: {response:?}"
69+
);
70+
}
71+
}
72+
73+
let mut response = (status, body).into_response();
74+
response
75+
.headers_mut()
76+
.insert("Content-Type", HeaderValue::from_static("application/json"));
77+
Ok(response)
78+
}
79+
}
80+
81+
impl RpcProviderFactory<DrpcConfig> for DrpcProvider {
82+
#[tracing::instrument(level = "debug")]
83+
fn new(provider_config: &DrpcConfig) -> Self {
84+
let forward_proxy_client = Client::builder().build::<_, hyper::Body>(HttpsConnector::new());
85+
let supported_chains: HashMap<String, String> = provider_config
86+
.supported_chains
87+
.iter()
88+
.map(|(k, v)| (k.clone(), v.0.clone()))
89+
.collect();
90+
91+
DrpcProvider {
92+
client: forward_proxy_client,
93+
supported_chains,
94+
}
95+
}
96+
}

src/providers/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ mod berachain;
5555
mod binance;
5656
mod bungee;
5757
mod coinbase;
58+
mod drpc;
5859
mod dune;
5960
mod getblock;
6061
mod infura;
@@ -84,6 +85,7 @@ pub use {
8485
berachain::BerachainProvider,
8586
binance::BinanceProvider,
8687
bungee::BungeeProvider,
88+
drpc::DrpcProvider,
8789
dune::DuneProvider,
8890
getblock::GetBlockProvider,
8991
infura::{InfuraProvider, InfuraWsProvider},
@@ -630,6 +632,7 @@ pub enum ProviderKind {
630632
Tenderly,
631633
Dune,
632634
Wemix,
635+
Drpc,
633636
}
634637

635638
impl Display for ProviderKind {
@@ -663,6 +666,7 @@ impl Display for ProviderKind {
663666
ProviderKind::Morph => "Morph",
664667
ProviderKind::Tenderly => "Tenderly",
665668
ProviderKind::Dune => "Dune",
669+
ProviderKind::Drpc => "Drpc",
666670
}
667671
)
668672
}
@@ -697,6 +701,7 @@ impl ProviderKind {
697701
"Tenderly" => Some(Self::Tenderly),
698702
"Dune" => Some(Self::Dune),
699703
"Wemix" => Some(Self::Wemix),
704+
"Drpc" => Some(Self::Drpc),
700705
_ => None,
701706
}
702707
}

tests/functional/http/drpc.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use {
2+
super::check_if_rpc_is_responding_correctly_for_solana, crate::context::ServerContext,
3+
rpc_proxy::providers::ProviderKind, test_context::test_context,
4+
};
5+
6+
#[test_context(ServerContext)]
7+
#[tokio::test]
8+
#[ignore]
9+
async fn drpc_provider_solana(ctx: &mut ServerContext) {
10+
let provider = ProviderKind::Drpc;
11+
12+
// Solana mainnet
13+
check_if_rpc_is_responding_correctly_for_solana(
14+
ctx,
15+
"5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
16+
&provider,
17+
)
18+
.await;
19+
}

tests/functional/http/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) mod aurora;
1111
pub(crate) mod base;
1212
pub(crate) mod berachain;
1313
pub(crate) mod binance;
14+
pub(crate) mod drpc;
1415
pub(crate) mod getblock;
1516
pub(crate) mod infura;
1617
pub(crate) mod lava;

0 commit comments

Comments
 (0)