penumbra_sdk_shielded_pool/component/rpc/
bank_query.rs1use std::collections::BTreeMap;
2
3use anyhow::Context;
4use async_trait::async_trait;
5use futures::StreamExt;
6use ibc_proto::cosmos::bank::v1beta1::{
7 query_server::Query as BankQuery, QueryAllBalancesRequest, QueryAllBalancesResponse,
8 QueryBalanceRequest, QueryBalanceResponse, QueryParamsRequest, QueryParamsResponse,
9 QueryTotalSupplyRequest, QueryTotalSupplyResponse,
10};
11use ibc_proto::cosmos::bank::v1beta1::{
12 QueryDenomMetadataByQueryStringRequest, QueryDenomMetadataByQueryStringResponse,
13 QueryDenomMetadataRequest, QueryDenomMetadataResponse, QueryDenomOwnersByQueryRequest,
14 QueryDenomOwnersByQueryResponse, QueryDenomOwnersRequest, QueryDenomOwnersResponse,
15 QueryDenomsMetadataRequest, QueryDenomsMetadataResponse, QuerySendEnabledRequest,
16 QuerySendEnabledResponse, QuerySpendableBalanceByDenomRequest,
17 QuerySpendableBalanceByDenomResponse, QuerySpendableBalancesRequest,
18 QuerySpendableBalancesResponse, QuerySupplyOfRequest, QuerySupplyOfResponse,
19};
20use penumbra_sdk_asset::asset::{self, Metadata};
21use penumbra_sdk_ibc::component::state_key as ibc_state_key;
22use penumbra_sdk_num::Amount;
23use penumbra_sdk_proto::StateReadProto as _;
24use tracing::instrument;
25
26use crate::component::AssetRegistryRead as _;
27use crate::state_key;
28
29use super::Server;
30
31#[async_trait]
32impl BankQuery for Server {
33 #[instrument(skip(self, _request))]
40 async fn total_supply(
41 &self,
42 _request: tonic::Request<QueryTotalSupplyRequest>,
43 ) -> Result<tonic::Response<QueryTotalSupplyResponse>, tonic::Status> {
44 let snapshot = self.storage.latest_snapshot();
45
46 let s = snapshot.prefix(state_key::denom_metadata_by_asset::prefix());
48 let mut total_supply = s
49 .filter_map(move |i: anyhow::Result<(String, Metadata)>| async move {
50 if i.is_err() {
51 return Some(Err(i.context("bad denom in state").err().unwrap()));
52 }
53 let (_key, denom_metadata) = i.expect("should not be an error");
54
55 Some(Ok((denom_metadata, Amount::from(0u32))))
57 })
58 .collect::<Vec<_>>()
59 .await
60 .into_iter()
61 .collect::<anyhow::Result<Vec<_>>>()
62 .map_err(|e| tonic::Status::internal(e.to_string()))?
63 .into_iter()
64 .collect::<BTreeMap<_, _>>();
65
66 let s = snapshot.prefix(ibc_state_key::ics20_value_balance::prefix());
67 let ibc_amounts = s
68 .filter_map(move |i: anyhow::Result<(String, Amount)>| async move {
69 if i.is_err() {
70 return Some(Err(i.context("bad amount in state").err().unwrap()));
71 }
72 let (key, amount) = i.expect("should not be an error");
73
74 let asset_id = key.split('/').last();
76 if asset_id.is_none() {
77 return Some(Err(asset_id
78 .context("bad IBC ics20 value balance key in state")
79 .err()
80 .unwrap()));
81 }
82 let asset_id = asset_id.expect("should not be an error");
83
84 let asset_id = asset_id.parse::<asset::Id>();
86 if asset_id.is_err() {
87 return Some(Err(asset_id
88 .context("invalid IBC ics20 value balance asset ID in state")
89 .err()
90 .unwrap()));
91 }
92 let asset_id = asset_id.expect("should not be an error");
93
94 Some(Ok((asset_id, amount)))
95 })
96 .collect::<Vec<_>>()
97 .await
98 .into_iter()
99 .collect::<anyhow::Result<Vec<_>>>()
100 .map_err(|e| tonic::Status::internal(e.to_string()))?;
101
102 for (asset_id, amount) in ibc_amounts {
104 let denom_metadata = snapshot.denom_metadata_by_asset(&asset_id).await;
105 if denom_metadata.is_none() {
106 continue;
109 }
110 let denom_metadata = denom_metadata.expect("should not be an error");
111
112 total_supply
114 .entry(denom_metadata)
115 .and_modify(|a| *a += amount)
116 .or_insert(amount);
117 }
118
119 Ok(tonic::Response::new(QueryTotalSupplyResponse {
120 pagination: None,
122 supply: total_supply
123 .into_iter()
124 .map(
125 |(denom_metadata, amount)| ibc_proto::cosmos::base::v1beta1::Coin {
126 denom: denom_metadata.to_string(),
127 amount: amount.to_string(),
128 },
129 )
130 .collect::<Vec<ibc_proto::cosmos::base::v1beta1::Coin>>(),
131 }))
132 }
133
134 async fn params(
135 &self,
136 _: tonic::Request<QueryParamsRequest>,
137 ) -> std::result::Result<tonic::Response<QueryParamsResponse>, tonic::Status> {
138 Err(tonic::Status::unimplemented("not implemented"))
139 }
140
141 async fn balance(
142 &self,
143 _: tonic::Request<QueryBalanceRequest>,
144 ) -> std::result::Result<tonic::Response<QueryBalanceResponse>, tonic::Status> {
145 Err(tonic::Status::unimplemented(
146 "not implemented, penumbra is a shielded chain",
147 ))
148 }
149
150 async fn all_balances(
151 &self,
152 _: tonic::Request<QueryAllBalancesRequest>,
153 ) -> std::result::Result<tonic::Response<QueryAllBalancesResponse>, tonic::Status> {
154 Err(tonic::Status::unimplemented(
155 "not implemented, penumbra is a shielded chain",
156 ))
157 }
158
159 async fn spendable_balances(
160 &self,
161 _: tonic::Request<QuerySpendableBalancesRequest>,
162 ) -> std::result::Result<tonic::Response<QuerySpendableBalancesResponse>, tonic::Status> {
163 Err(tonic::Status::unimplemented(
164 "not implemented, penumbra is a shielded chain",
165 ))
166 }
167
168 async fn spendable_balance_by_denom(
169 &self,
170 _: tonic::Request<QuerySpendableBalanceByDenomRequest>,
171 ) -> std::result::Result<tonic::Response<QuerySpendableBalanceByDenomResponse>, tonic::Status>
172 {
173 Err(tonic::Status::unimplemented(
174 "not implemented, penumbra is a shielded chain",
175 ))
176 }
177
178 async fn supply_of(
179 &self,
180 _: tonic::Request<QuerySupplyOfRequest>,
181 ) -> std::result::Result<tonic::Response<QuerySupplyOfResponse>, tonic::Status> {
182 Err(tonic::Status::unimplemented("not implemented"))
183 }
184
185 async fn denom_metadata(
186 &self,
187 _: tonic::Request<QueryDenomMetadataRequest>,
188 ) -> std::result::Result<tonic::Response<QueryDenomMetadataResponse>, tonic::Status> {
189 Err(tonic::Status::unimplemented("not implemented"))
190 }
191
192 async fn denoms_metadata(
193 &self,
194 _: tonic::Request<QueryDenomsMetadataRequest>,
195 ) -> std::result::Result<tonic::Response<QueryDenomsMetadataResponse>, tonic::Status> {
196 Err(tonic::Status::unimplemented("not implemented"))
197 }
198
199 async fn denom_owners(
200 &self,
201 _: tonic::Request<QueryDenomOwnersRequest>,
202 ) -> std::result::Result<tonic::Response<QueryDenomOwnersResponse>, tonic::Status> {
203 Err(tonic::Status::unimplemented("not implemented"))
204 }
205
206 async fn send_enabled(
207 &self,
208 _: tonic::Request<QuerySendEnabledRequest>,
209 ) -> std::result::Result<tonic::Response<QuerySendEnabledResponse>, tonic::Status> {
210 Err(tonic::Status::unimplemented("not implemented"))
211 }
212
213 async fn denom_metadata_by_query_string(
214 &self,
215 _: tonic::Request<QueryDenomMetadataByQueryStringRequest>,
216 ) -> Result<tonic::Response<QueryDenomMetadataByQueryStringResponse>, tonic::Status> {
217 Err(tonic::Status::unimplemented("not implemented"))
218 }
219
220 async fn denom_owners_by_query(
221 &self,
222 _: tonic::Request<QueryDenomOwnersByQueryRequest>,
223 ) -> Result<tonic::Response<QueryDenomOwnersByQueryResponse>, tonic::Status> {
224 Err(tonic::Status::unimplemented("not implemented"))
225 }
226}