Skip to main content

qorechain/query/
rest.rs

1//! Cosmos + QoreChain REST (LCD) read client.
2
3use crate::error::{Error, Result};
4use crate::query::DEFAULT_USER_AGENT;
5use serde_json::Value;
6
7/// A Cosmos + QoreChain REST read client.
8#[derive(Debug, Clone)]
9pub struct RestClient {
10    base_url: String,
11    http: reqwest::Client,
12}
13
14impl RestClient {
15    /// Creates a `RestClient` for the given base URL using a fresh HTTP client.
16    pub fn new(base_url: impl Into<String>) -> Self {
17        Self::with_client(base_url, reqwest::Client::new())
18    }
19
20    /// Creates a `RestClient` for the given base URL using the supplied HTTP
21    /// client (e.g. one shared across the SDK).
22    pub fn with_client(base_url: impl Into<String>, http: reqwest::Client) -> Self {
23        let base = base_url.into();
24        Self {
25            base_url: base.trim_end_matches('/').to_string(),
26            http,
27        }
28    }
29
30    /// The configured base URL (without a trailing slash).
31    pub fn base_url(&self) -> &str {
32        &self.base_url
33    }
34
35    fn join(&self, path: &str) -> String {
36        format!("{}/{}", self.base_url, path.trim_start_matches('/'))
37    }
38
39    /// Generic GET escape hatch for any documented REST route. The response body
40    /// is parsed into a [`serde_json::Value`].
41    pub async fn get(&self, path: &str, query: &[(&str, &str)]) -> Result<Value> {
42        let url = self.join(path);
43        let mut req = self
44            .http
45            .get(&url)
46            .header("Accept", "application/json")
47            .header("User-Agent", DEFAULT_USER_AGENT);
48        let filtered: Vec<(&str, &str)> = query
49            .iter()
50            .copied()
51            .filter(|(_, v)| !v.is_empty())
52            .collect();
53        if !filtered.is_empty() {
54            req = req.query(&filtered);
55        }
56        let resp = req.send().await?;
57        let status = resp.status();
58        let body = resp.text().await?;
59        if !status.is_success() {
60            return Err(Error::Http {
61                status: status.as_u16(),
62                url,
63                body,
64            });
65        }
66        serde_json::from_str(&body).map_err(|e| Error::InvalidResponse(e.to_string()))
67    }
68
69    /// Returns all balances for a Cosmos account.
70    pub async fn get_all_balances(&self, address: &str) -> Result<Value> {
71        self.get(&format!("/cosmos/bank/v1beta1/balances/{address}"), &[])
72            .await
73    }
74
75    /// Returns the AI engine statistics.
76    pub async fn get_ai_stats(&self) -> Result<Value> {
77        self.get("/qorechain/ai/v1/stats", &[]).await
78    }
79
80    /// Returns an AI-assisted fee estimate for the given urgency
81    /// (`"fast"`, `"normal"`, `"slow"`).
82    pub async fn get_fee_estimate(&self, urgency: &str) -> Result<Value> {
83        self.get("/qorechain/ai/v1/fee-estimate", &[("urgency", urgency)])
84            .await
85    }
86
87    /// Returns the supported bridge chains.
88    pub async fn get_bridge_chains(&self) -> Result<Value> {
89        self.get("/qorechain/bridge/v1/chains", &[]).await
90    }
91
92    /// Returns the PQC account record for an address.
93    pub async fn get_pqc_account(&self, address: &str) -> Result<Value> {
94        self.get(&format!("/qorechain/pqc/v1/accounts/{address}"), &[])
95            .await
96    }
97
98    /// Returns the reputation record for a validator address.
99    pub async fn get_reputation(&self, validator_address: &str) -> Result<Value> {
100        self.get(
101            &format!("/qorechain/reputation/v1/validators/{validator_address}"),
102            &[],
103        )
104        .await
105    }
106
107    /// Returns the token burn statistics.
108    pub async fn get_burn_stats(&self) -> Result<Value> {
109        self.get("/qorechain/burn/v1/stats", &[]).await
110    }
111
112    /// Returns the xQORE position for an address.
113    pub async fn get_xqore_position(&self, address: &str) -> Result<Value> {
114        self.get(&format!("/qorechain/xqore/v1/position/{address}"), &[])
115            .await
116    }
117
118    /// Returns the current inflation rate.
119    pub async fn get_inflation_rate(&self) -> Result<Value> {
120        self.get("/qorechain/inflation/v1/rate", &[]).await
121    }
122}