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
}
pub async fn multilayer_params(
&self,
) -> Result<qorechain::multilayer::v1::QueryParamsResponse> {
self.grpc_query(
"/qorechain.multilayer.v1.Query/Params",
&qorechain::multilayer::v1::QueryParamsRequest {},
)
.await
}
pub async fn multilayer_layer(
&self,
layer_id: impl Into<String>,
) -> Result<qorechain::multilayer::v1::QueryLayerResponse> {
self.grpc_query(
"/qorechain.multilayer.v1.Query/Layer",
&qorechain::multilayer::v1::QueryLayerRequest {
layer_id: layer_id.into(),
},
)
.await
}
pub async fn multilayer_layers(
&self,
) -> Result<qorechain::multilayer::v1::QueryLayersResponse> {
self.grpc_query(
"/qorechain.multilayer.v1.Query/Layers",
&qorechain::multilayer::v1::QueryLayersRequest {},
)
.await
}
pub async fn multilayer_anchor(
&self,
layer_id: impl Into<String>,
) -> Result<qorechain::multilayer::v1::QueryAnchorResponse> {
self.grpc_query(
"/qorechain.multilayer.v1.Query/Anchor",
&qorechain::multilayer::v1::QueryAnchorRequest {
layer_id: layer_id.into(),
},
)
.await
}
pub async fn multilayer_anchors(
&self,
layer_id: impl Into<String>,
) -> Result<qorechain::multilayer::v1::QueryAnchorsResponse> {
self.grpc_query(
"/qorechain.multilayer.v1.Query/Anchors",
&qorechain::multilayer::v1::QueryAnchorsRequest {
layer_id: layer_id.into(),
},
)
.await
}
pub async fn multilayer_routing_stats(
&self,
) -> Result<qorechain::multilayer::v1::QueryRoutingStatsView> {
self.grpc_query(
"/qorechain.multilayer.v1.Query/RoutingStats",
&qorechain::multilayer::v1::QueryRoutingStatsRequest {},
)
.await
}
pub async fn amm_params(&self) -> Result<qorechain::amm::v1::QueryParamsResponse> {
self.grpc_query(
"/qorechain.amm.v1.Query/Params",
&qorechain::amm::v1::QueryParamsRequest {},
)
.await
}
pub async fn amm_pool(&self, pool_id: u64) -> Result<qorechain::amm::v1::QueryPoolResponse> {
self.grpc_query(
"/qorechain.amm.v1.Query/Pool",
&qorechain::amm::v1::QueryPoolRequest { pool_id },
)
.await
}
pub async fn amm_pools(&self) -> Result<qorechain::amm::v1::QueryPoolsResponse> {
self.grpc_query(
"/qorechain.amm.v1.Query/Pools",
&qorechain::amm::v1::QueryPoolsRequest {},
)
.await
}
pub async fn amm_pool_by_denoms(
&self,
denom_a: impl Into<String>,
denom_b: impl Into<String>,
) -> Result<qorechain::amm::v1::QueryPoolByDenomsResponse> {
self.grpc_query(
"/qorechain.amm.v1.Query/PoolByDenoms",
&qorechain::amm::v1::QueryPoolByDenomsRequest {
denom_a: denom_a.into(),
denom_b: denom_b.into(),
},
)
.await
}
pub async fn amm_lp_balance(
&self,
pool_id: u64,
address: impl Into<String>,
) -> Result<qorechain::amm::v1::QueryLpBalanceResponse> {
self.grpc_query(
"/qorechain.amm.v1.Query/LPBalance",
&qorechain::amm::v1::QueryLpBalanceRequest {
pool_id,
address: address.into(),
},
)
.await
}
pub async fn amm_quote_exact_in(
&self,
pool_id: u64,
denom_in: impl Into<String>,
amount_in: impl Into<String>,
) -> Result<qorechain::amm::v1::QueryQuoteExactInResponse> {
self.grpc_query(
"/qorechain.amm.v1.Query/QuoteExactIn",
&qorechain::amm::v1::QueryQuoteExactInRequest {
pool_id,
denom_in: denom_in.into(),
amount_in: amount_in.into(),
},
)
.await
}
pub async fn amm_quote_exact_out(
&self,
pool_id: u64,
denom_out: impl Into<String>,
amount_out: impl Into<String>,
) -> Result<qorechain::amm::v1::QueryQuoteExactOutResponse> {
self.grpc_query(
"/qorechain.amm.v1.Query/QuoteExactOut",
&qorechain::amm::v1::QueryQuoteExactOutRequest {
pool_id,
denom_out: denom_out.into(),
amount_out: amount_out.into(),
},
)
.await
}
pub async fn license_check(
&self,
grantee: impl Into<String>,
feature_id: impl Into<String>,
) -> Result<qorechain::license::v1::QueryCheckResponse> {
self.grpc_query(
"/qorechain.license.v1.Query/Check",
&qorechain::license::v1::QueryCheckRequest {
grantee: grantee.into(),
feature_id: feature_id.into(),
},
)
.await
}
pub async fn license_holders(
&self,
feature_id: impl Into<String>,
) -> Result<qorechain::license::v1::QueryHoldersResponse> {
self.grpc_query(
"/qorechain.license.v1.Query/Holders",
&qorechain::license::v1::QueryHoldersRequest {
feature_id: feature_id.into(),
},
)
.await
}
pub async fn license_list(
&self,
grantee: impl Into<String>,
) -> Result<qorechain::license::v1::QueryListResponse> {
self.grpc_query(
"/qorechain.license.v1.Query/List",
&qorechain::license::v1::QueryListRequest {
grantee: grantee.into(),
},
)
.await
}
pub async fn abstractaccount_config(
&self,
) -> Result<qorechain::abstractaccount::v1::QueryConfigResponse> {
self.grpc_query(
"/qorechain.abstractaccount.v1.Query/Config",
&qorechain::abstractaccount::v1::QueryConfigRequest {},
)
.await
}
pub async fn abstractaccount_account(
&self,
address: impl Into<String>,
) -> Result<qorechain::abstractaccount::v1::QueryAccountResponse> {
self.grpc_query(
"/qorechain.abstractaccount.v1.Query/Account",
&qorechain::abstractaccount::v1::QueryAccountRequest {
address: address.into(),
},
)
.await
}
pub async fn abstractaccount_accounts(
&self,
) -> Result<qorechain::abstractaccount::v1::QueryAccountsResponse> {
self.grpc_query(
"/qorechain.abstractaccount.v1.Query/Accounts",
&qorechain::abstractaccount::v1::QueryAccountsRequest {},
)
.await
}
pub async fn abstractaccount_permission_schema(
&self,
) -> Result<qorechain::abstractaccount::v1::QueryPermissionSchemaResponse> {
self.grpc_query(
"/qorechain.abstractaccount.v1.Query/PermissionSchema",
&qorechain::abstractaccount::v1::QueryPermissionSchemaRequest {},
)
.await
}
pub async fn rdk_params(&self) -> Result<qorechain::rdk::v1::QueryParamsResponse> {
self.grpc_query(
"/qorechain.rdk.v1.Query/Params",
&qorechain::rdk::v1::QueryParamsRequest {},
)
.await
}
pub async fn rdk_rollup(
&self,
rollup_id: impl Into<String>,
) -> Result<qorechain::rdk::v1::QueryRollupResponse> {
self.grpc_query(
"/qorechain.rdk.v1.Query/Rollup",
&qorechain::rdk::v1::QueryRollupRequest {
rollup_id: rollup_id.into(),
},
)
.await
}
pub async fn rdk_rollups(&self) -> Result<qorechain::rdk::v1::QueryRollupsResponse> {
self.grpc_query(
"/qorechain.rdk.v1.Query/Rollups",
&qorechain::rdk::v1::QueryRollupsRequest {},
)
.await
}
pub async fn rdk_batch(
&self,
rollup_id: impl Into<String>,
batch_index: u64,
) -> Result<qorechain::rdk::v1::QueryBatchResponse> {
self.grpc_query(
"/qorechain.rdk.v1.Query/Batch",
&qorechain::rdk::v1::QueryBatchRequest {
rollup_id: rollup_id.into(),
batch_index,
},
)
.await
}
pub async fn rdk_latest_batch(
&self,
rollup_id: impl Into<String>,
) -> Result<qorechain::rdk::v1::QueryLatestBatchResponse> {
self.grpc_query(
"/qorechain.rdk.v1.Query/LatestBatch",
&qorechain::rdk::v1::QueryLatestBatchRequest {
rollup_id: rollup_id.into(),
},
)
.await
}
pub async fn bridge_config(&self) -> Result<qorechain::bridge::v1::QueryConfigResponse> {
self.grpc_query(
"/qorechain.bridge.v1.Query/Config",
&qorechain::bridge::v1::QueryConfigRequest {},
)
.await
}
pub async fn bridge_chain_config(
&self,
chain_id: impl Into<String>,
) -> Result<qorechain::bridge::v1::QueryChainConfigResponse> {
self.grpc_query(
"/qorechain.bridge.v1.Query/ChainConfig",
&qorechain::bridge::v1::QueryChainConfigRequest {
chain_id: chain_id.into(),
},
)
.await
}
pub async fn bridge_chain_configs(
&self,
) -> Result<qorechain::bridge::v1::QueryChainConfigsResponse> {
self.grpc_query(
"/qorechain.bridge.v1.Query/ChainConfigs",
&qorechain::bridge::v1::QueryChainConfigsRequest {},
)
.await
}
pub async fn bridge_validator(
&self,
address: impl Into<String>,
) -> Result<qorechain::bridge::v1::QueryValidatorResponse> {
self.grpc_query(
"/qorechain.bridge.v1.Query/Validator",
&qorechain::bridge::v1::QueryValidatorRequest {
address: address.into(),
},
)
.await
}
pub async fn bridge_validators(
&self,
) -> Result<qorechain::bridge::v1::QueryValidatorsResponse> {
self.grpc_query(
"/qorechain.bridge.v1.Query/Validators",
&qorechain::bridge::v1::QueryValidatorsRequest {},
)
.await
}
pub async fn bridge_operation(
&self,
id: impl Into<String>,
) -> Result<qorechain::bridge::v1::QueryOperationResponse> {
self.grpc_query(
"/qorechain.bridge.v1.Query/Operation",
&qorechain::bridge::v1::QueryOperationRequest { id: id.into() },
)
.await
}
pub async fn bridge_operations(
&self,
) -> Result<qorechain::bridge::v1::QueryOperationsResponse> {
self.grpc_query(
"/qorechain.bridge.v1.Query/Operations",
&qorechain::bridge::v1::QueryOperationsRequest {},
)
.await
}
}