layer_climb_core/querier/
basic.rs1use tracing::instrument;
2
3use crate::prelude::*;
4
5use super::ConnectionMode;
6
7impl QueryClient {
8 #[instrument]
9 pub async fn balance(&self, addr: Address, denom: Option<String>) -> Result<Option<u128>> {
10 self.run_with_middleware(BalanceReq { addr, denom }).await
11 }
12
13 #[instrument]
14 pub async fn all_balances(
15 &self,
16 addr: Address,
17 limit_per_page: Option<u64>,
18 ) -> Result<Vec<layer_climb_proto::Coin>> {
19 self.run_with_middleware(AllBalancesReq {
20 addr,
21 limit_per_page,
22 })
23 .await
24 }
25
26 #[instrument]
27 pub async fn base_account(
28 &self,
29 addr: &Address,
30 ) -> Result<layer_climb_proto::auth::BaseAccount> {
31 self.run_with_middleware(BaseAccountReq { addr: addr.clone() })
32 .await
33 }
34
35 #[instrument]
36 pub async fn staking_params(&self) -> Result<layer_climb_proto::staking::Params> {
37 self.run_with_middleware(StakingParamsReq {}).await
38 }
39
40 #[instrument]
41 pub async fn block(&self, height: Option<u64>) -> Result<BlockResp> {
42 self.run_with_middleware(BlockReq { height }).await
43 }
44
45 #[instrument]
46 pub async fn block_header(&self, height: Option<u64>) -> Result<BlockHeaderResp> {
47 self.run_with_middleware(BlockHeaderReq { height }).await
48 }
49
50 #[instrument]
51 pub async fn block_height(&self) -> Result<u64> {
52 self.run_with_middleware(BlockHeightReq {}).await
53 }
54
55 #[instrument]
56 pub async fn node_info(&self) -> Result<layer_climb_proto::tendermint::GetNodeInfoResponse> {
57 self.run_with_middleware(NodeInfoReq {}).await
58 }
59}
60
61#[derive(Clone, Debug)]
62pub struct BalanceReq {
63 pub addr: Address,
64 pub denom: Option<String>,
65}
66
67impl QueryRequest for BalanceReq {
68 type QueryResponse = Option<u128>;
69
70 async fn request(&self, client: QueryClient) -> Result<Self::QueryResponse> {
71 let denom = self
72 .denom
73 .clone()
74 .unwrap_or(client.chain_config.gas_denom.clone());
75
76 let req = layer_climb_proto::bank::QueryBalanceRequest {
77 address: self.addr.to_string(),
78 denom,
79 };
80
81 let coin = match client.get_connection_mode() {
82 ConnectionMode::Grpc => {
83 let mut query_client = layer_climb_proto::bank::query_client::QueryClient::new(
84 client.clone_grpc_channel()?,
85 );
86
87 query_client
88 .balance(req)
89 .await
90 .map(|res| res.into_inner().balance)?
91 }
92 ConnectionMode::Rpc => client
93 .rpc_client()?
94 .abci_protobuf_query::<_, layer_climb_proto::bank::QueryBalanceResponse>(
95 "/cosmos.bank.v1beta1.Query/Balance",
96 req,
97 None,
98 )
99 .await
100 .map(|res| res.balance)?,
101 };
102
103 match coin {
104 None => Ok(None),
105 Some(coin) => {
106 let amount = coin
107 .amount
108 .parse::<u128>()
109 .context("couldn't parse amount")?;
110 Ok(Some(amount))
111 }
112 }
113 }
114}
115
116#[derive(Clone, Debug)]
117pub struct AllBalancesReq {
118 pub addr: Address,
119 pub limit_per_page: Option<u64>,
120}
121
122impl QueryRequest for AllBalancesReq {
123 type QueryResponse = Vec<layer_climb_proto::Coin>;
124
125 async fn request(&self, client: QueryClient) -> Result<Self::QueryResponse> {
126 let mut coins = Vec::new();
127
128 let mut pagination = None;
129
130 let limit = self
131 .limit_per_page
132 .unwrap_or(client.balances_pagination_limit);
133
134 let mut grpc_query_client = match client.get_connection_mode() {
135 ConnectionMode::Grpc => Some(layer_climb_proto::bank::query_client::QueryClient::new(
136 client.clone_grpc_channel()?,
137 )),
138 ConnectionMode::Rpc => None,
139 };
140
141 let height = match client.get_connection_mode() {
143 ConnectionMode::Grpc => None,
144 ConnectionMode::Rpc => Some(BlockHeightReq {}.request(client.clone()).await?),
145 };
146
147 loop {
148 let req = layer_climb_proto::bank::QueryAllBalancesRequest {
149 address: self.addr.to_string(),
150 pagination,
151 resolve_denom: true,
152 };
153
154 let resp = match client.get_connection_mode() {
155 ConnectionMode::Grpc => grpc_query_client
156 .as_mut()
157 .unwrap()
158 .all_balances(req)
159 .await
160 .map(|res| res.into_inner())?,
161 ConnectionMode::Rpc => client
162 .rpc_client()?
163 .abci_protobuf_query::<_, layer_climb_proto::bank::QueryAllBalancesResponse>(
164 "/cosmos.bank.v1beta1.Query/AllBalances",
165 req,
166 height,
167 )
168 .await?,
169 };
170
171 coins.extend(resp.balances);
172
173 match &resp.pagination {
174 None => break,
175 Some(pagination_response) => {
176 if pagination_response.next_key.is_empty() {
177 break;
178 }
179 }
180 }
181
182 pagination = resp
183 .pagination
184 .map(|p| layer_climb_proto::query::PageRequest {
185 key: p.next_key,
186 offset: 0,
187 limit,
188 count_total: false,
189 reverse: false,
190 });
191 }
192
193 Ok(coins)
194 }
195}
196
197#[derive(Clone, Debug)]
198pub struct BaseAccountReq {
199 pub addr: Address,
200}
201
202impl QueryRequest for BaseAccountReq {
203 type QueryResponse = layer_climb_proto::auth::BaseAccount;
204
205 async fn request(&self, client: QueryClient) -> Result<Self::QueryResponse> {
206 let req = layer_climb_proto::auth::QueryAccountRequest {
207 address: self.addr.to_string(),
208 };
209
210 let query_resp = match client.get_connection_mode() {
211 ConnectionMode::Grpc => {
212 let mut query_client = layer_climb_proto::auth::query_client::QueryClient::new(
213 client.clone_grpc_channel()?,
214 );
215
216 query_client
217 .account(req)
218 .await
219 .map(|res| res.into_inner().account)
220 .map_err(|err| {
221 anyhow::Error::from(err).context(format!("account {} not found", self.addr))
222 })?
223 .ok_or_else(|| anyhow!("account {} not found", self.addr))?
224 }
225 ConnectionMode::Rpc => client
226 .rpc_client()?
227 .abci_protobuf_query::<_, layer_climb_proto::auth::QueryAccountResponse>(
228 "/cosmos.auth.v1beta1.Query/Account",
229 req,
230 None,
231 )
232 .await?
233 .account
234 .ok_or_else(|| anyhow!("account {} not found", self.addr))?,
235 };
236
237 let account = layer_climb_proto::auth::BaseAccount::decode(query_resp.value.as_slice())
238 .context("couldn't decode account")?;
239
240 Ok(account)
241 }
242}
243
244#[derive(Clone, Debug)]
245pub struct StakingParamsReq {}
246
247impl QueryRequest for StakingParamsReq {
248 type QueryResponse = layer_climb_proto::staking::Params;
249
250 async fn request(&self, client: QueryClient) -> Result<layer_climb_proto::staking::Params> {
251 let resp = match client.get_connection_mode() {
252 ConnectionMode::Grpc => {
253 let mut query_client = layer_climb_proto::staking::query_client::QueryClient::new(
254 client.clone_grpc_channel()?,
255 );
256
257 query_client
258 .params(layer_climb_proto::staking::QueryParamsRequest {})
259 .await
260 .map(|res| res.into_inner())
261 .context("couldn't get staking params")?
262 }
263 ConnectionMode::Rpc => {
264 client
265 .rpc_client()?
266 .abci_protobuf_query::<_, layer_climb_proto::staking::QueryParamsResponse>(
267 "/cosmos.staking.v1beta1.Query/Params",
268 layer_climb_proto::staking::QueryParamsRequest {},
269 None,
270 )
271 .await?
272 }
273 };
274
275 resp.params.ok_or(anyhow!("no staking params found"))
276 }
277}
278
279#[derive(Clone, Debug)]
280pub struct NodeInfoReq {}
281
282impl QueryRequest for NodeInfoReq {
283 type QueryResponse = layer_climb_proto::tendermint::GetNodeInfoResponse;
284
285 async fn request(
286 &self,
287 client: QueryClient,
288 ) -> Result<layer_climb_proto::tendermint::GetNodeInfoResponse> {
289 let req = layer_climb_proto::tendermint::GetNodeInfoRequest {};
290
291 match client.get_connection_mode() {
292 ConnectionMode::Grpc => {
293 layer_climb_proto::tendermint::service_client::ServiceClient::new(
294 client.clone_grpc_channel()?,
295 )
296 .get_node_info(req)
297 .await
298 .map(|resp| resp.into_inner())
299 .context("couldn't get node info")
300 }
301 ConnectionMode::Rpc => client
302 .rpc_client()?
303 .abci_protobuf_query::<_, layer_climb_proto::tendermint::GetNodeInfoResponse>(
304 "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo",
305 req,
306 None,
307 )
308 .await
309 .context("couldn't get node info"),
310 }
311 }
312}
313
314#[derive(Clone, Debug)]
315pub struct BlockReq {
316 pub height: Option<u64>,
317}
318
319#[derive(Debug)]
320pub enum BlockResp {
321 Sdk(layer_climb_proto::block::SdkBlock),
322 Old(layer_climb_proto::block::TendermintBlock),
323}
324
325impl QueryRequest for BlockReq {
326 type QueryResponse = BlockResp;
327
328 async fn request(&self, client: QueryClient) -> Result<Self::QueryResponse> {
329 let height = self.height;
330
331 match client.get_connection_mode() {
332 ConnectionMode::Grpc => {
333 let mut query_client =
334 layer_climb_proto::tendermint::service_client::ServiceClient::new(
335 client.clone_grpc_channel()?,
336 );
337
338 match height {
339 Some(height) => query_client
340 .get_block_by_height(
341 layer_climb_proto::tendermint::GetBlockByHeightRequest {
342 height: height.try_into()?,
343 },
344 )
345 .await
346 .map_err(|err| err.into())
347 .and_then(|res| {
348 let res = res.into_inner();
349 match res.sdk_block {
350 Some(block) => Ok(BlockResp::Sdk(block)),
351 None => res
352 .block
353 .map(BlockResp::Old)
354 .ok_or(anyhow!("no block found")),
355 }
356 }),
357 None => query_client
358 .get_latest_block(layer_climb_proto::tendermint::GetLatestBlockRequest {})
359 .await
360 .map_err(|err| err.into())
361 .and_then(|res| {
362 let res = res.into_inner();
363 match res.sdk_block {
364 Some(block) => Ok(BlockResp::Sdk(block)),
365 None => res
366 .block
367 .map(BlockResp::Old)
368 .ok_or(anyhow!("no block found")),
369 }
370 }),
371 }
372 .with_context(move || match height {
373 Some(height) => format!("no block found at height {height}"),
374 None => "no latest block found".to_string(),
375 })
376 }
377 ConnectionMode::Rpc => {
378 let resp = client.rpc_client()?.block(height).await?;
379
380 Ok(BlockResp::Old(resp.block.into()))
381 }
382 }
383 }
384}
385
386#[derive(Clone, Debug)]
387pub struct BlockHeaderReq {
388 pub height: Option<u64>,
389}
390
391#[derive(Debug)]
392pub enum BlockHeaderResp {
393 Sdk(layer_climb_proto::block::SdkHeader),
394 Old(layer_climb_proto::block::TendermintHeader),
395}
396
397impl BlockHeaderResp {
398 pub fn height(&self) -> Result<u64> {
399 Ok(match self {
400 BlockHeaderResp::Sdk(header) => header.height.try_into()?,
401 BlockHeaderResp::Old(header) => header.height.try_into()?,
402 })
403 }
404
405 pub fn time(&self) -> Option<layer_climb_proto::Timestamp> {
406 match self {
407 BlockHeaderResp::Sdk(header) => header.time,
408 BlockHeaderResp::Old(header) => header.time.map(|time| layer_climb_proto::Timestamp {
409 seconds: time.seconds,
410 nanos: time.nanos,
411 }),
412 }
413 }
414
415 pub fn app_hash(&self) -> Vec<u8> {
416 match self {
417 BlockHeaderResp::Sdk(header) => header.app_hash.clone(),
418 BlockHeaderResp::Old(header) => header.app_hash.clone(),
419 }
420 }
421
422 pub fn next_validators_hash(&self) -> Vec<u8> {
423 match self {
424 BlockHeaderResp::Sdk(header) => header.next_validators_hash.clone(),
425 BlockHeaderResp::Old(header) => header.next_validators_hash.clone(),
426 }
427 }
428}
429
430impl QueryRequest for BlockHeaderReq {
431 type QueryResponse = BlockHeaderResp;
432
433 async fn request(&self, client: QueryClient) -> Result<Self::QueryResponse> {
434 let block = BlockReq {
435 height: self.height,
436 }
437 .request(client)
438 .await?;
439
440 match block {
441 BlockResp::Sdk(block) => Ok(BlockHeaderResp::Sdk(
442 block.header.context("no header found")?,
443 )),
444 BlockResp::Old(block) => Ok(BlockHeaderResp::Old(
445 block.header.context("no header found")?,
446 )),
447 }
448 }
449}
450
451#[derive(Clone, Debug)]
452pub struct BlockHeightReq {}
453
454impl QueryRequest for BlockHeightReq {
455 type QueryResponse = u64;
456
457 async fn request(&self, client: QueryClient) -> Result<u64> {
458 let header = BlockHeaderReq { height: None }.request(client).await?;
459
460 Ok(match header {
461 BlockHeaderResp::Sdk(header) => header.height,
462 BlockHeaderResp::Old(header) => header.height,
463 }
464 .try_into()?)
465 }
466}