use crate::error::{Error, Result};
use crate::proto::qorechain;
use crate::query::JsonRpcClient;
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine;
use prost::Message;
use serde_json::json;
#[derive(Debug, Clone)]
pub struct TypedQueryClient {
rpc: JsonRpcClient,
}
impl TypedQueryClient {
pub fn new(rpc_url: impl Into<String>) -> Self {
Self {
rpc: JsonRpcClient::new(rpc_url),
}
}
pub fn with_rpc(rpc: JsonRpcClient) -> Self {
Self { rpc }
}
async fn grpc_query<Req: Message, Resp: Message + Default>(
&self,
path: &str,
req: &Req,
) -> Result<Resp> {
let data_hex = hex::encode(req.encode_to_vec());
let params = json!({
"path": path,
"data": data_hex,
"prove": false,
});
let result = self.rpc.call("abci_query", params).await?;
let response = &result["response"];
if let Some(code) = response["code"].as_u64() {
if code != 0 {
let log = response["log"].as_str().unwrap_or("query failed");
return Err(Error::InvalidResponse(format!(
"abci_query {path} failed (code {code}): {log}"
)));
}
}
let value_b64 = response["value"].as_str().unwrap_or("");
let bytes = if value_b64.is_empty() {
Vec::new()
} else {
BASE64
.decode(value_b64)
.map_err(|e| Error::InvalidResponse(format!("decode abci value: {e}")))?
};
Resp::decode(bytes.as_slice())
.map_err(|e| Error::InvalidResponse(format!("decode {path} response: {e}")))
}
pub async fn pqc_account(
&self,
address: impl Into<String>,
) -> Result<qorechain::pqc::v1::QueryAccountResponse> {
self.grpc_query(
"/qorechain.pqc.v1.Query/Account",
&qorechain::pqc::v1::QueryAccountRequest {
address: address.into(),
},
)
.await
}
pub async fn crossvm_params(&self) -> Result<qorechain::crossvm::v1::QueryParamsResponse> {
self.grpc_query(
"/qorechain.crossvm.v1.Query/Params",
&qorechain::crossvm::v1::QueryParamsRequest {},
)
.await
}
pub async fn crossvm_pending_messages(
&self,
) -> Result<qorechain::crossvm::v1::QueryPendingMessagesResponse> {
self.grpc_query(
"/qorechain.crossvm.v1.Query/PendingMessages",
&qorechain::crossvm::v1::QueryPendingMessagesRequest {},
)
.await
}
pub async fn crossvm_message(
&self,
id: impl Into<String>,
) -> Result<qorechain::crossvm::v1::QueryMessageResponse> {
self.grpc_query(
"/qorechain.crossvm.v1.Query/Message",
&qorechain::crossvm::v1::QueryMessageRequest { id: id.into() },
)
.await
}
pub async fn lightnode(
&self,
address: impl Into<String>,
) -> Result<qorechain::lightnode::v1::QueryLightNodeResponse> {
self.grpc_query(
"/qorechain.lightnode.v1.Query/LightNode",
&qorechain::lightnode::v1::QueryLightNodeRequest {
address: address.into(),
},
)
.await
}
pub async fn lightnodes(&self) -> Result<qorechain::lightnode::v1::QueryLightNodesResponse> {
self.grpc_query(
"/qorechain.lightnode.v1.Query/LightNodes",
&qorechain::lightnode::v1::QueryLightNodesRequest {},
)
.await
}
pub async fn lightnode_params(&self) -> Result<qorechain::lightnode::v1::QueryParamsResponse> {
self.grpc_query(
"/qorechain.lightnode.v1.Query/Params",
&qorechain::lightnode::v1::QueryParamsRequest {},
)
.await
}
pub async fn lightnode_rewards(
&self,
address: impl Into<String>,
) -> Result<qorechain::lightnode::v1::QueryRewardsResponse> {
self.grpc_query(
"/qorechain.lightnode.v1.Query/Rewards",
&qorechain::lightnode::v1::QueryRewardsRequest {
address: address.into(),
},
)
.await
}
pub async fn lightnode_stats(&self) -> Result<qorechain::lightnode::v1::QueryStatsResponse> {
self.grpc_query(
"/qorechain.lightnode.v1.Query/Stats",
&qorechain::lightnode::v1::QueryStatsRequest {},
)
.await
}
pub async fn svm_slot(&self) -> Result<qorechain::svm::v1::QuerySlotResponse> {
self.grpc_query(
"/qorechain.svm.v1.Query/Slot",
&qorechain::svm::v1::QuerySlotRequest {},
)
.await
}
pub async fn svm_account(
&self,
address: impl Into<String>,
) -> Result<qorechain::svm::v1::QueryAccountResponse> {
self.grpc_query(
"/qorechain.svm.v1.Query/Account",
&qorechain::svm::v1::QueryAccountRequest {
address: address.into(),
},
)
.await
}
pub async fn svm_program(
&self,
address: impl Into<String>,
) -> Result<qorechain::svm::v1::QueryProgramResponse> {
self.grpc_query(
"/qorechain.svm.v1.Query/Program",
&qorechain::svm::v1::QueryProgramRequest {
address: address.into(),
},
)
.await
}
pub async fn reputation_params(
&self,
) -> Result<qorechain::reputation::v1::QueryParamsResponse> {
self.grpc_query(
"/qorechain.reputation.v1.Query/Params",
&qorechain::reputation::v1::QueryParamsRequest {},
)
.await
}
pub async fn qca_config(&self) -> Result<qorechain::qca::v1::QueryConfigResponse> {
self.grpc_query(
"/qorechain.qca.v1.Query/Config",
&qorechain::qca::v1::QueryConfigRequest {},
)
.await
}
pub async fn rlconsensus_agent_status(
&self,
) -> Result<qorechain::rlconsensus::v1::QueryAgentStatusResponse> {
self.grpc_query(
"/qorechain.rlconsensus.v1.Query/AgentStatus",
&qorechain::rlconsensus::v1::QueryAgentStatusRequest {},
)
.await
}
pub async fn rlconsensus_params(
&self,
) -> Result<qorechain::rlconsensus::v1::QueryParamsResponse> {
self.grpc_query(
"/qorechain.rlconsensus.v1.Query/Params",
&qorechain::rlconsensus::v1::QueryParamsRequest {},
)
.await
}
pub async fn rlconsensus_observation(
&self,
) -> Result<qorechain::rlconsensus::v1::QueryObservationResponse> {
self.grpc_query(
"/qorechain.rlconsensus.v1.Query/Observation",
&qorechain::rlconsensus::v1::QueryObservationRequest {},
)
.await
}
pub async fn rlconsensus_reward(
&self,
) -> Result<qorechain::rlconsensus::v1::QueryRewardResponse> {
self.grpc_query(
"/qorechain.rlconsensus.v1.Query/Reward",
&qorechain::rlconsensus::v1::QueryRewardRequest {},
)
.await
}
pub async fn rlconsensus_policy(
&self,
) -> Result<qorechain::rlconsensus::v1::QueryPolicyResponse> {
self.grpc_query(
"/qorechain.rlconsensus.v1.Query/Policy",
&qorechain::rlconsensus::v1::QueryPolicyRequest {},
)
.await
}
}