1use reqwest::{Client, Error, StatusCode};
5use serde::de::DeserializeOwned;
6
7use crate::enums::solscan_endpoints::SolscanEndpoints;
8use crate::enums::solscan_errors::SolscanError;
9use crate::r#const::SOLSCANBASEURL;
10use crate::structs::account_info::AccountInfo;
11use crate::structs::block_result::BlockResult;
12use crate::structs::chain_info::ChainInfo;
13use crate::structs::sol_transfer::SolTransferList;
14use crate::structs::spl_transfer::SplTransfer;
15use crate::structs::token::Token;
16use crate::structs::token_holder::TokenHolders;
17use crate::structs::token_market_item::TokenMarketItem;
18use crate::structs::token_meta::TokenMeta;
19use crate::structs::transaction::Transaction;
20use crate::structs::transaction_last::TransactionLast;
21use crate::structs::transaction_list_item::TransactionListItem;
22
23pub struct SolscanAPI {
33 base_url: String,
34 client: Client,
35}
36
37
38impl Default for SolscanAPI {
40 fn default() -> Self {
46 SolscanAPI::new()
47 }
48}
49
50
51impl SolscanAPI {
53 pub fn new() -> SolscanAPI {
59 SolscanAPI {
60 base_url: SOLSCANBASEURL.parse().unwrap(),
61 client: Client::new(),
62 }
63 }
64 pub fn new_with_url(solscan_url: String) -> SolscanAPI {
75 SolscanAPI {
76 base_url: solscan_url,
77 client: Client::new(),
78 }
79 }
80
81 async fn fetch(&self, url_path: String) -> Result<String, SolscanError> {
94 println!("{:?}", self.base_url.to_string() + url_path.as_str());
95 match {
96 self.client.get(self.base_url.to_string() + url_path.as_str())
97 .header("User-Agent", "Mozilla/5.0")
98 .send()
99 .await
100 } {
101 Ok(response) => {
102 if response.status() == 200 {
103 match response.text().await {
104 Ok(text) => { Ok(text) }
105 Err(e) => {
106 println!("{:?}", e);
107 Err(SolscanError::APICConversionError)
108 }
109 }
110 } else {
111 println!("API-Status Code is: {:?}", response.status());
112 Err(SolscanError::APIWrongStatusCode)
113 }
114 }
115 Err(e) => {
116 println!("{:?}", e);
117 Err(SolscanError::APIError)
118 }
119 }
120 }
121
122 async fn solscan_fetch<T: DeserializeOwned>(&self, endpoint: SolscanEndpoints, url_endpoint: &str) -> Result<T, SolscanError> {
133 match {
134 self.fetch(
135 match endpoint {
136 SolscanEndpoints::BlockLast => endpoint.value().to_owned() + url_endpoint,
137 SolscanEndpoints::BlockTransactions => endpoint.value().to_owned() + url_endpoint,
138 SolscanEndpoints::Block => endpoint.value().to_owned() + url_endpoint,
139 SolscanEndpoints::TransactionLast => endpoint.value().to_owned() + url_endpoint,
140 SolscanEndpoints::Transaction => endpoint.value().to_owned() + url_endpoint,
141 SolscanEndpoints::AccountTokens => endpoint.value().to_owned() + url_endpoint,
142 SolscanEndpoints::AccountTransaction => endpoint.value().to_owned() + url_endpoint,
143 SolscanEndpoints::AccountStakeAccounts => endpoint.value().to_owned() + url_endpoint,
144 SolscanEndpoints::AccountSPLTransfers => endpoint.value().to_owned() + url_endpoint,
145 SolscanEndpoints::AccountSolTransfers => endpoint.value().to_owned() + url_endpoint,
146 SolscanEndpoints::Account => endpoint.value().to_owned() + url_endpoint,
147 SolscanEndpoints::TokenHolders => endpoint.value().to_owned() + url_endpoint,
148 SolscanEndpoints::TokenMeta => endpoint.value().to_owned() + url_endpoint,
149 SolscanEndpoints::MarketToken => endpoint.value().to_owned() + url_endpoint,
150 SolscanEndpoints::ChainInfo => endpoint.value().to_owned() + url_endpoint,
151 _ => { "none".to_string() }
152 }
153 ).await
154 } {
155 Ok(api_data) => {
156 match serde_json::from_str::<T>(api_data.as_str()) {
157 Ok(api_data) => {
158 Ok(api_data)
159 }
160 Err(e) => {
161 println!("{:?}", e);
162 Err(SolscanError::SerializeError)
163 }
164 }
165 }
166 Err(e) => {
167 println!("{:?}", e);
168 Err(SolscanError::APIError)
169 }
170 }
171 }
172 pub async fn ping_status(&self, endpoint: Option<String>) -> Result<StatusCode, Error> {
187 Ok(self.client.get(self.base_url.to_string() + endpoint.unwrap_or_default().as_str()).header("User-Agent", "Mozilla/5.0").send().await?.status())
188 }
189 pub async fn get_block_last(&self, limit: Option<i64>) -> Result<Vec<BlockResult>, SolscanError> {
202 let mut url_endpoint: String = "".to_string();
203 if limit.is_some() {
204 url_endpoint += &*format!("?limit={}", limit.unwrap());
205 }
206 self.solscan_fetch::<Vec<BlockResult>>(SolscanEndpoints::BlockLast, url_endpoint.as_str()).await
207 }
208 pub async fn get_block_transactions(&self, block: i64, offset: Option<i64>, limit: Option<i64>) -> Result<Vec<TransactionLast>, SolscanError> {
220 let mut url_endpoint: String = format!("?block={}", block);
221 if offset.is_some() {
222 url_endpoint += &*format!("&offset={}", offset.unwrap());
223 }
224 if limit.is_some() {
225 url_endpoint += &*format!("&limit={}", limit.unwrap());
226 }
227 self.solscan_fetch::<Vec<TransactionLast>>(SolscanEndpoints::BlockTransactions, url_endpoint.as_str()).await
228 }
229 pub async fn get_block_block(&self, block: i64) -> Result<BlockResult, SolscanError> {
239 let url_endpoint: String = format!("/{}", block);
240 self.solscan_fetch::<BlockResult>(SolscanEndpoints::Block, url_endpoint.as_str()).await
241 }
242 pub async fn get_transaction_last(&self, limit: Option<i64>) -> Result<Vec<TransactionLast>, SolscanError> {
255 let mut url_endpoint: String = "".to_string();
256 if limit.is_some() {
257 url_endpoint += &*format!("?limit={}", limit.unwrap())
258 }
259 self.solscan_fetch::<Vec<TransactionLast>>(SolscanEndpoints::TransactionLast, url_endpoint.as_str()).await
260 }
261 pub async fn get_transaction(&self, signature: &str) -> Result<Transaction, SolscanError> {
271 let url_endpoint: String = format!("/{}", signature);
272 self.solscan_fetch::<Transaction>(SolscanEndpoints::Transaction, url_endpoint.as_str()).await
273 }
274 pub async fn get_account_tokens(&self, account: &str) -> Result<Vec<Token>, SolscanError> {
287 let url_endpoint: String = format!("?account={}", account);
288 self.solscan_fetch::<Vec<Token>>(SolscanEndpoints::AccountTokens, url_endpoint.as_str()).await
289 }
290 pub async fn get_account_transactions(&self, account: &str, before_hash: Option<String>, limit: Option<i64>) -> Result<Vec<TransactionListItem>, SolscanError> {
302 let mut url_endpoint: String = format!("?account={}", account);
303 if before_hash.is_some() {
304 url_endpoint += &*format!("&beforeHash={}", before_hash.unwrap())
305 }
306 if limit.is_some() {
307 url_endpoint += &*format!("&limit={}", limit.unwrap())
308 }
309 self.solscan_fetch::<Vec<TransactionListItem>>(SolscanEndpoints::AccountTransaction, url_endpoint.as_str()).await
310 }
311 pub async fn get_account_stake_accounts(&self, account: &str) -> Result<Vec<Token>, SolscanError> {
321 let url_endpoint: String = format!("?account={}", account);
322 self.solscan_fetch::<Vec<Token>>(SolscanEndpoints::AccountStakeAccounts, url_endpoint.as_str()).await
323 }
324 pub async fn get_account_spl_transfer(&self, account: &str, form_time: Option<u64>, to_time: Option<u64>, offset: Option<i64>, limit: Option<i64>) -> Result<SplTransfer, SolscanError> {
338 let mut url_endpoint: String = format!("?account={}", account);
339 if form_time.is_some() {
340 url_endpoint += &*format!("&form_time={}", form_time.unwrap())
341 }
342 if to_time.is_some() {
343 url_endpoint += &*format!("&to_time={}", to_time.unwrap())
344 }
345 if offset.is_some() {
346 url_endpoint += &*format!("&offset={}", offset.unwrap())
347 }
348 if limit.is_some() {
349 url_endpoint += &*format!("&limit={}", limit.unwrap())
350 }
351 self.solscan_fetch::<SplTransfer>(SolscanEndpoints::AccountSPLTransfers, url_endpoint.as_str()).await
352 }
353 pub async fn get_account_sol_transfer(&self, account: &str, form_time: Option<u64>, to_time: Option<u64>, offset: Option<i64>, limit: Option<i64>) -> Result<SolTransferList, SolscanError> {
367 let mut url_endpoint: String = format!("?account={}", account);
368 if form_time.is_some() {
369 url_endpoint += &*format!("&form_time={}", form_time.unwrap())
370 }
371 if to_time.is_some() {
372 url_endpoint += &*format!("&to_time={}", to_time.unwrap())
373 }
374 if offset.is_some() {
375 url_endpoint += &*format!("&offset={}", offset.unwrap())
376 }
377 if limit.is_some() {
378 url_endpoint += &*format!("&limit={}", limit.unwrap())
379 }
380 self.solscan_fetch::<SolTransferList>(SolscanEndpoints::AccountSolTransfers, url_endpoint.as_str()).await
381 }
382 pub async fn get_account_account(&self, account: &str) -> Result<AccountInfo, SolscanError> {
392 let url_endpoint: String = format!("/{}", account);
393 self.solscan_fetch::<AccountInfo>(SolscanEndpoints::Account, url_endpoint.as_str()).await
394 }
395 pub async fn get_token_holders(&self, account: &str, offset: Option<i64>, limit: Option<i64>) -> Result<TokenHolders, SolscanError> {
410 let mut url_endpoint: String = format!("?tokenAddress={}", account);
411 if offset.is_some() {
412 url_endpoint += &*format!("&offset={}", offset.unwrap())
413 }
414 if limit.is_some() {
415 url_endpoint += &*format!("&limit={}", limit.unwrap())
416 }
417 self.solscan_fetch::<TokenHolders>(SolscanEndpoints::TokenHolders, url_endpoint.as_str()).await
418 }
419 pub async fn get_token_meta(&self, account: &str) -> Result<TokenMeta, SolscanError> {
429 let url_endpoint: String = format!("?tokenAddress={}", account);
430 self.solscan_fetch::<TokenMeta>(SolscanEndpoints::TokenMeta, url_endpoint.as_str()).await
431 }
432 pub async fn get_market_token(&self, account: &str) -> Result<TokenMarketItem, SolscanError> {
445 let url_endpoint: String = format!("/{}", account);
446 self.solscan_fetch::<TokenMarketItem>(SolscanEndpoints::MarketToken, url_endpoint.as_str()).await
447 }
448 pub async fn get_chain_info(&self) -> Result<ChainInfo, SolscanError> {
457 let url_endpoint: String = "/".to_string();
458 self.solscan_fetch::<ChainInfo>(SolscanEndpoints::ChainInfo, url_endpoint.as_str()).await
459 }
460 }
462
463#[cfg(test)]
464pub mod test_solscan_inner {
465 use crate::r#const::SOLSCANBASEURL;
466 use crate::solscan::SolscanAPI;
467
468 #[test]
469 fn test_init_baseurl() {
470 let solscan_api = SolscanAPI::new();
471 assert_eq!(solscan_api.base_url, SOLSCANBASEURL);
472 }
473}