1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use crate::error::SubscanComponentResult;
use crate::types::ExtrinsicsData;
use crate::types::OpenPrice;
use crate::types::SubscanResponse;

#[derive(Clone, Debug)]
pub struct Subscan {
    /// HTTP Client
    http: reqwest::blocking::Client,
    endpoint: String,
    token: String,
}

impl Subscan {
    pub fn new(http: reqwest::blocking::Client, endpoint: String, token: String) -> Self {
        Self {
            http,
            endpoint,
            token,
        }
    }
}

impl Subscan {
    pub fn endpoint(mut self, endpoint: impl AsRef<str>) -> Self {
        self.endpoint = endpoint.as_ref().to_string();
        self
    }

    pub fn token(mut self, token: impl AsRef<str>) -> Self {
        self.token = token.as_ref().to_string();
        self
    }
}

impl Subscan {
    async fn _post<T: serde::de::DeserializeOwned>(
        &self,
        api: impl AsRef<str>,
        data_json_string: impl AsRef<str>,
    ) -> SubscanComponentResult<T> {
        let api = format!("{}{}", self.endpoint, api.as_ref());
        tracing::trace!(target: "component-subscan", "POST {} ---> {}", api, data_json_string.as_ref());
        let data = serde_json::from_str::<serde_json::Value>(data_json_string.as_ref())?;
        let value = self
            .http
            .post(api)
            .header("X-API-Key", &self.token)
            .header("Content-Type", "application/json")
            .json(&data)
            .send()?
            .text()?;
        tracing::trace!(target: "component-subscan", "<--- {}", value);
        Ok(serde_json::from_str(&value)?)
    }

    pub fn _endpoint(&self) -> &String {
        &self.endpoint
    }

    // https://docs.api.subscan.io/#extrinsics
    pub async fn extrinsics(
        &self,
        page: u32,
        row: u32,
    ) -> SubscanComponentResult<SubscanResponse<ExtrinsicsData>> {
        let data = format!(r#"{{"row": {},"page": {}, "signed": "signed"}}"#, row, page);
        self._post("/api/scan/extrinsics", data).await
    }

    // https://docs.api.subscan.io/#price
    pub async fn price(&self, time: u64) -> SubscanComponentResult<SubscanResponse<OpenPrice>> {
        let data = format!(r#"{{"time": {}}}"#, time);
        self._post("/api/open/price", data).await
    }
}