1use crate::error::{Error, Result};
13use crate::proto::qorechain;
14use crate::query::JsonRpcClient;
15use base64::engine::general_purpose::STANDARD as BASE64;
16use base64::Engine;
17use prost::Message;
18use serde_json::json;
19
20#[derive(Debug, Clone)]
22pub struct TypedQueryClient {
23 rpc: JsonRpcClient,
24}
25
26impl TypedQueryClient {
27 pub fn new(rpc_url: impl Into<String>) -> Self {
29 Self {
30 rpc: JsonRpcClient::new(rpc_url),
31 }
32 }
33
34 pub fn with_rpc(rpc: JsonRpcClient) -> Self {
36 Self { rpc }
37 }
38
39 async fn grpc_query<Req: Message, Resp: Message + Default>(
42 &self,
43 path: &str,
44 req: &Req,
45 ) -> Result<Resp> {
46 let data_hex = hex::encode(req.encode_to_vec());
47 let params = json!({
48 "path": path,
49 "data": data_hex,
50 "prove": false,
51 });
52 let result = self.rpc.call("abci_query", params).await?;
53 let response = &result["response"];
54 if let Some(code) = response["code"].as_u64() {
56 if code != 0 {
57 let log = response["log"].as_str().unwrap_or("query failed");
58 return Err(Error::InvalidResponse(format!(
59 "abci_query {path} failed (code {code}): {log}"
60 )));
61 }
62 }
63 let value_b64 = response["value"].as_str().unwrap_or("");
64 let bytes = if value_b64.is_empty() {
65 Vec::new()
66 } else {
67 BASE64
68 .decode(value_b64)
69 .map_err(|e| Error::InvalidResponse(format!("decode abci value: {e}")))?
70 };
71 Resp::decode(bytes.as_slice())
72 .map_err(|e| Error::InvalidResponse(format!("decode {path} response: {e}")))
73 }
74
75 pub async fn pqc_account(
79 &self,
80 address: impl Into<String>,
81 ) -> Result<qorechain::pqc::v1::QueryAccountResponse> {
82 self.grpc_query(
83 "/qorechain.pqc.v1.Query/Account",
84 &qorechain::pqc::v1::QueryAccountRequest {
85 address: address.into(),
86 },
87 )
88 .await
89 }
90
91 pub async fn crossvm_params(&self) -> Result<qorechain::crossvm::v1::QueryParamsResponse> {
95 self.grpc_query(
96 "/qorechain.crossvm.v1.Query/Params",
97 &qorechain::crossvm::v1::QueryParamsRequest {},
98 )
99 .await
100 }
101
102 pub async fn crossvm_pending_messages(
104 &self,
105 ) -> Result<qorechain::crossvm::v1::QueryPendingMessagesResponse> {
106 self.grpc_query(
107 "/qorechain.crossvm.v1.Query/PendingMessages",
108 &qorechain::crossvm::v1::QueryPendingMessagesRequest {},
109 )
110 .await
111 }
112
113 pub async fn crossvm_message(
115 &self,
116 id: impl Into<String>,
117 ) -> Result<qorechain::crossvm::v1::QueryMessageResponse> {
118 self.grpc_query(
119 "/qorechain.crossvm.v1.Query/Message",
120 &qorechain::crossvm::v1::QueryMessageRequest { id: id.into() },
121 )
122 .await
123 }
124
125 pub async fn lightnode(
129 &self,
130 address: impl Into<String>,
131 ) -> Result<qorechain::lightnode::v1::QueryLightNodeResponse> {
132 self.grpc_query(
133 "/qorechain.lightnode.v1.Query/LightNode",
134 &qorechain::lightnode::v1::QueryLightNodeRequest {
135 address: address.into(),
136 },
137 )
138 .await
139 }
140
141 pub async fn lightnodes(&self) -> Result<qorechain::lightnode::v1::QueryLightNodesResponse> {
143 self.grpc_query(
144 "/qorechain.lightnode.v1.Query/LightNodes",
145 &qorechain::lightnode::v1::QueryLightNodesRequest {},
146 )
147 .await
148 }
149
150 pub async fn lightnode_params(&self) -> Result<qorechain::lightnode::v1::QueryParamsResponse> {
152 self.grpc_query(
153 "/qorechain.lightnode.v1.Query/Params",
154 &qorechain::lightnode::v1::QueryParamsRequest {},
155 )
156 .await
157 }
158
159 pub async fn lightnode_rewards(
161 &self,
162 address: impl Into<String>,
163 ) -> Result<qorechain::lightnode::v1::QueryRewardsResponse> {
164 self.grpc_query(
165 "/qorechain.lightnode.v1.Query/Rewards",
166 &qorechain::lightnode::v1::QueryRewardsRequest {
167 address: address.into(),
168 },
169 )
170 .await
171 }
172
173 pub async fn lightnode_stats(&self) -> Result<qorechain::lightnode::v1::QueryStatsResponse> {
175 self.grpc_query(
176 "/qorechain.lightnode.v1.Query/Stats",
177 &qorechain::lightnode::v1::QueryStatsRequest {},
178 )
179 .await
180 }
181
182 pub async fn svm_slot(&self) -> Result<qorechain::svm::v1::QuerySlotResponse> {
186 self.grpc_query(
187 "/qorechain.svm.v1.Query/Slot",
188 &qorechain::svm::v1::QuerySlotRequest {},
189 )
190 .await
191 }
192
193 pub async fn svm_account(
195 &self,
196 address: impl Into<String>,
197 ) -> Result<qorechain::svm::v1::QueryAccountResponse> {
198 self.grpc_query(
199 "/qorechain.svm.v1.Query/Account",
200 &qorechain::svm::v1::QueryAccountRequest {
201 address: address.into(),
202 },
203 )
204 .await
205 }
206
207 pub async fn svm_program(
209 &self,
210 address: impl Into<String>,
211 ) -> Result<qorechain::svm::v1::QueryProgramResponse> {
212 self.grpc_query(
213 "/qorechain.svm.v1.Query/Program",
214 &qorechain::svm::v1::QueryProgramRequest {
215 address: address.into(),
216 },
217 )
218 .await
219 }
220
221 pub async fn reputation_params(
225 &self,
226 ) -> Result<qorechain::reputation::v1::QueryParamsResponse> {
227 self.grpc_query(
228 "/qorechain.reputation.v1.Query/Params",
229 &qorechain::reputation::v1::QueryParamsRequest {},
230 )
231 .await
232 }
233
234 pub async fn qca_config(&self) -> Result<qorechain::qca::v1::QueryConfigResponse> {
238 self.grpc_query(
239 "/qorechain.qca.v1.Query/Config",
240 &qorechain::qca::v1::QueryConfigRequest {},
241 )
242 .await
243 }
244
245 pub async fn rlconsensus_agent_status(
249 &self,
250 ) -> Result<qorechain::rlconsensus::v1::QueryAgentStatusResponse> {
251 self.grpc_query(
252 "/qorechain.rlconsensus.v1.Query/AgentStatus",
253 &qorechain::rlconsensus::v1::QueryAgentStatusRequest {},
254 )
255 .await
256 }
257
258 pub async fn rlconsensus_params(
260 &self,
261 ) -> Result<qorechain::rlconsensus::v1::QueryParamsResponse> {
262 self.grpc_query(
263 "/qorechain.rlconsensus.v1.Query/Params",
264 &qorechain::rlconsensus::v1::QueryParamsRequest {},
265 )
266 .await
267 }
268
269 pub async fn rlconsensus_observation(
271 &self,
272 ) -> Result<qorechain::rlconsensus::v1::QueryObservationResponse> {
273 self.grpc_query(
274 "/qorechain.rlconsensus.v1.Query/Observation",
275 &qorechain::rlconsensus::v1::QueryObservationRequest {},
276 )
277 .await
278 }
279
280 pub async fn rlconsensus_reward(
282 &self,
283 ) -> Result<qorechain::rlconsensus::v1::QueryRewardResponse> {
284 self.grpc_query(
285 "/qorechain.rlconsensus.v1.Query/Reward",
286 &qorechain::rlconsensus::v1::QueryRewardRequest {},
287 )
288 .await
289 }
290
291 pub async fn rlconsensus_policy(
293 &self,
294 ) -> Result<qorechain::rlconsensus::v1::QueryPolicyResponse> {
295 self.grpc_query(
296 "/qorechain.rlconsensus.v1.Query/Policy",
297 &qorechain::rlconsensus::v1::QueryPolicyRequest {},
298 )
299 .await
300 }
301}