1use crate::{Client, EtherscanError, Response, Result};
2use chrono::{DateTime, NaiveDate, NaiveTime, TimeZone, Utc};
3use ethers_core::{types::U256, utils::parse_units};
4use serde::{Deserialize, Deserializer};
5use std::str::FromStr;
6
7#[derive(Deserialize, Clone, Debug)]
8#[serde(rename_all = "PascalCase")]
9pub struct EthSupply2 {
10 #[serde(deserialize_with = "deserialize_number_from_string")]
12 #[serde(rename = "EthSupply")]
13 pub eth_supply: u128,
14 #[serde(deserialize_with = "deserialize_number_from_string")]
16 #[serde(rename = "Eth2Staking")]
17 pub eth2_staking: u128,
18 #[serde(deserialize_with = "deser_wei_amount")]
20 #[serde(rename = "BurntFees")]
21 pub burnt_fees: U256,
22 #[serde(deserialize_with = "deserialize_number_from_string")]
24 #[serde(rename = "WithdrawnTotal")]
25 pub withdrawn_total: u128,
26}
27
28#[derive(Deserialize, Clone, Debug)]
29pub struct EthPrice {
30 #[serde(deserialize_with = "deserialize_number_from_string")]
32 pub ethbtc: f64,
33 #[serde(deserialize_with = "deserialize_datetime_from_string")]
35 pub ethbtc_timestamp: DateTime<Utc>,
36 #[serde(deserialize_with = "deserialize_number_from_string")]
38 pub ethusd: f64,
39 #[serde(deserialize_with = "deserialize_datetime_from_string")]
41 pub ethusd_timestamp: DateTime<Utc>,
42}
43
44#[derive(Deserialize, Clone, Debug)]
45pub struct NodeCount {
46 #[serde(rename = "UTCDate")]
48 #[serde(deserialize_with = "deserialize_utc_date_from_string")]
49 pub utc_date: DateTime<Utc>,
50 #[serde(rename = "TotalNodeCount")]
52 #[serde(deserialize_with = "deserialize_number_from_string")]
53 pub total_node_count: usize,
54}
55
56fn deser_wei_amount<'de, D>(deserializer: D) -> Result<U256, D::Error>
60where
61 D: Deserializer<'de>,
62{
63 #[derive(Deserialize)]
64 #[serde(untagged)]
65 enum StringOrInt {
66 Number(u64),
67 String(String),
68 }
69
70 match StringOrInt::deserialize(deserializer)? {
71 StringOrInt::Number(i) => Ok(U256::from(i)),
72 StringOrInt::String(s) => {
73 parse_units(s, "wei").map(Into::into).map_err(serde::de::Error::custom)
74 }
75 }
76}
77
78fn deserialize_number_from_string<'de, T, D>(deserializer: D) -> Result<T, D::Error>
79where
80 D: Deserializer<'de>,
81 T: FromStr + serde::Deserialize<'de>,
82 <T as FromStr>::Err: std::fmt::Display,
83{
84 #[derive(Deserialize)]
85 #[serde(untagged)]
86 enum StringOrInt<T> {
87 String(String),
88 Number(T),
89 }
90
91 match StringOrInt::<T>::deserialize(deserializer)? {
92 StringOrInt::String(s) => s.parse::<T>().map_err(serde::de::Error::custom),
93 StringOrInt::Number(i) => Ok(i),
94 }
95}
96
97fn deserialize_utc_date_from_string<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
98where
99 D: Deserializer<'de>,
100{
101 let s: String = Deserialize::deserialize(deserializer)?;
102
103 let naive_date = NaiveDate::parse_from_str(&s, "%Y-%m-%d").expect("Invalid date format");
104 let naive_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap();
105
106 Ok(DateTime::<Utc>::from_naive_utc_and_offset(naive_date.and_time(naive_time), Utc))
107}
108
109fn deserialize_datetime_from_string<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
110where
111 D: Deserializer<'de>,
112{
113 #[derive(Deserialize)]
114 #[serde(untagged)]
115 enum StringOrInt {
116 String(String),
117 Number(i64),
118 }
119
120 match StringOrInt::deserialize(deserializer)? {
121 StringOrInt::String(s) => {
122 let i = s.parse::<i64>().unwrap();
123 Ok(Utc.timestamp_opt(i, 0).unwrap())
124 }
125 StringOrInt::Number(i) => Ok(Utc.timestamp_opt(i, 0).unwrap()),
126 }
127}
128
129impl Client {
130 pub async fn eth_supply(&self) -> Result<u128> {
133 let query = self.create_query("stats", "ethsupply", serde_json::Value::Null);
134 let response: Response<String> = self.get_json(&query).await?;
135
136 if response.status == "1" {
137 Ok(u128::from_str(&response.result).map_err(|_| EtherscanError::EthSupplyFailed)?)
138 } else {
139 Err(EtherscanError::EthSupplyFailed)
140 }
141 }
142
143 pub async fn eth_supply2(&self) -> Result<EthSupply2> {
146 let query = self.create_query("stats", "ethsupply2", serde_json::Value::Null);
147 let response: Response<EthSupply2> = self.get_json(&query).await?;
148
149 Ok(response.result)
150 }
151
152 pub async fn eth_price(&self) -> Result<EthPrice> {
154 let query = self.create_query("stats", "ethprice", serde_json::Value::Null);
155 let response: Response<EthPrice> = self.get_json(&query).await?;
156
157 Ok(response.result)
158 }
159
160 pub async fn node_count(&self) -> Result<NodeCount> {
162 let query = self.create_query("stats", "nodecount", serde_json::Value::Null);
163 let response: Response<NodeCount> = self.get_json(&query).await?;
164
165 Ok(response.result)
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn response_works() {
175 let v = r#"{
178 "status":"1",
179 "message":"OK",
180 "result":{
181 "EthSupply":"122373866217800000000000000",
182 "Eth2Staking":"1157529105115885000000000",
183 "BurntFees":"3102505506455601519229842",
184 "WithdrawnTotal":"1170200333006131000000000"
185 }
186 }"#;
187 let eth_supply2: Response<EthSupply2> = serde_json::from_str(v).unwrap();
188 assert_eq!(eth_supply2.message, "OK");
189 assert_eq!(eth_supply2.result.eth_supply, 122373866217800000000000000);
190 assert_eq!(eth_supply2.result.eth2_staking, 1157529105115885000000000);
191 assert_eq!(
192 eth_supply2.result.burnt_fees,
193 parse_units("3102505506455601519229842", "wei").map(Into::into).unwrap()
194 );
195 assert_eq!(eth_supply2.result.withdrawn_total, 1170200333006131000000000);
196
197 let v = r#"{
200 "status":"1",
201 "message":"OK",
202 "result":{
203 "ethbtc":"0.06116",
204 "ethbtc_timestamp":"1624961308",
205 "ethusd":"2149.18",
206 "ethusd_timestamp":"1624961308"
207 }
208 }"#;
209 let eth_price: Response<EthPrice> = serde_json::from_str(v).unwrap();
210 assert_eq!(eth_price.message, "OK");
211 assert_eq!(eth_price.result.ethbtc, 0.06116);
212 assert_eq!(eth_price.result.ethusd, 2149.18);
213
214 let v = r#"{
217 "status":"1",
218 "message":"OK",
219 "result":{
220 "UTCDate":"2021-06-29",
221 "TotalNodeCount":"6413"
222 }
223 }"#;
224 let node_count: Response<NodeCount> = serde_json::from_str(v).unwrap();
225 assert_eq!(node_count.message, "OK");
226 assert_eq!(node_count.result.total_node_count, 6413);
227 }
228}