gas_network_sdk/
client.rs1use crate::{
2 error::{GasNetworkError, Result},
3 types::*,
4};
5use reqwest::{Client, header::HeaderMap};
6use url::Url;
7
8const BASE_URL: &str = "https://api.blocknative.com";
9const RPC_URL: &str = "https://rpc.gas.network";
10
11#[derive(Debug, Clone)]
12pub struct GasNetworkClient {
13 client: Client,
14 api_key: String,
15 base_url: Url,
16 rpc_url: Url,
17}
18
19impl GasNetworkClient {
20 pub fn new(api_key: String) -> Result<Self> {
21 let mut headers = HeaderMap::new();
22 headers.insert("Authorization", format!("Bearer {}", api_key).parse().unwrap());
23 headers.insert("Content-Type", "application/json".parse().unwrap());
24
25 let client = Client::builder()
26 .default_headers(headers)
27 .build()?;
28
29 Ok(Self {
30 client,
31 api_key,
32 base_url: Url::parse(BASE_URL)?,
33 rpc_url: Url::parse(RPC_URL)?,
34 })
35 }
36
37 pub async fn get_gas_prices(&self, chain: Chain) -> Result<GasPriceResponse> {
38 let url = self.base_url
39 .join(&format!("/gasprices/blockprices?chain={}", chain.as_str()))?;
40
41 let response = self.client.get(url).send().await?;
42
43 if !response.status().is_success() {
44 let error_text = response.text().await?;
45 return Err(GasNetworkError::Api {
46 message: error_text,
47 });
48 }
49
50 let gas_prices: GasPriceResponse = response.json().await?;
51 Ok(gas_prices)
52 }
53
54 pub async fn get_base_fee_estimates(&self, chain: Chain) -> Result<BaseFeeResponse> {
55 if chain != Chain::Ethereum {
56 return Err(GasNetworkError::UnsupportedChain(
57 "Base fee estimates are only available for Ethereum".to_string()
58 ));
59 }
60
61 let url = self.base_url
62 .join("/gasprices/basefee-estimates")?;
63
64 let response = self.client.get(url).send().await?;
65
66 if !response.status().is_success() {
67 let error_text = response.text().await?;
68 return Err(GasNetworkError::Api {
69 message: error_text,
70 });
71 }
72
73 let base_fee: BaseFeeResponse = response.json().await?;
74 Ok(base_fee)
75 }
76
77 pub async fn get_gas_distribution(&self, chain: Chain) -> Result<DistributionResponse> {
78 if chain != Chain::Ethereum {
79 return Err(GasNetworkError::UnsupportedChain(
80 "Gas distribution is only available for Ethereum".to_string()
81 ));
82 }
83
84 let url = self.base_url
85 .join(&format!("/gasprices/distribution?chain={}", chain.as_str()))?;
86
87 let response = self.client.get(url).send().await?;
88
89 if !response.status().is_success() {
90 let error_text = response.text().await?;
91 return Err(GasNetworkError::Api {
92 message: error_text,
93 });
94 }
95
96 let distribution: DistributionResponse = response.json().await?;
97 Ok(distribution)
98 }
99
100 pub async fn get_oracle_data(&self, chain_id: u64) -> Result<OraclePayload> {
101 let url = self.rpc_url
102 .join(&format!("/oracle?chainId={}", chain_id))?;
103
104 let response = self.client.get(url).send().await?;
105
106 if !response.status().is_success() {
107 let error_text = response.text().await?;
108 return Err(GasNetworkError::Api {
109 message: error_text,
110 });
111 }
112
113 let oracle_data: OraclePayload = response.json().await?;
114 Ok(oracle_data)
115 }
116
117 pub async fn get_next_block_estimate(
118 &self,
119 chain: Chain,
120 confidence_level: Option<u8>,
121 ) -> Result<GasPriceEstimate> {
122 let gas_prices = self.get_gas_prices(chain).await?;
123
124 let confidence = confidence_level.unwrap_or(90);
125
126 if let Some(block_price) = gas_prices.block_prices.first() {
128 block_price
129 .estimated_prices
130 .iter()
131 .find(|estimate| estimate.confidence >= confidence)
132 .cloned()
133 .ok_or_else(|| GasNetworkError::Api {
134 message: format!("No estimate found for confidence level {}", confidence),
135 })
136 } else {
137 Err(GasNetworkError::Api {
138 message: "No block prices available".to_string(),
139 })
140 }
141 }
142
143 pub fn supported_chains() -> Vec<Chain> {
144 vec![
145 Chain::Ethereum,
146 Chain::Polygon,
147 Chain::Bitcoin,
148 Chain::Sei,
149 Chain::Optimism,
150 Chain::Arbitrum,
151 Chain::Base,
152 Chain::Linea,
153 Chain::Unichain,
154 ]
155 }
156
157 pub fn chains_supporting_base_fee() -> Vec<Chain> {
158 vec![Chain::Ethereum]
159 }
160
161 pub fn chains_supporting_distribution() -> Vec<Chain> {
162 vec![Chain::Ethereum]
163 }
164
165 pub fn api_key(&self) -> &str {
167 &self.api_key
168 }
169}