1use std::collections::HashMap;
36use std::fmt::{Debug, Display};
37
38use near_account_id::AccountId;
39use near_jsonrpc_client::methods::query::RpcQueryResponse;
40use near_jsonrpc_client::methods::{self, RpcMethod};
41use near_jsonrpc_primitives::types::chunks::ChunkReference;
42use near_jsonrpc_primitives::types::query::QueryResponseKind;
43use near_primitives::types::{BlockId, BlockReference, StoreKey};
44use near_primitives::views::{BlockView, QueryRequest};
45use near_token::NearToken;
46
47use crate::error::RpcErrorCode;
48use crate::operations::Function;
49use crate::result::ViewResultDetails;
50use crate::rpc::client::Client;
51use crate::rpc::{tool, BoxFuture};
52use crate::types::account::AccountDetails;
53use crate::types::{AccessKey, AccessKeyInfo, BlockHeight, Finality, PublicKey, ShardId};
54use crate::{Block, Chunk, CryptoHash, Result};
55
56pub struct Query<'a, T> {
61 pub(crate) method: T,
62 pub(crate) client: &'a Client,
63 pub(crate) block_ref: Option<BlockReference>,
64}
65
66impl<'a, T> Query<'a, T> {
67 pub(crate) fn new(client: &'a Client, method: T) -> Self {
68 Self {
69 method,
70 client,
71 block_ref: None,
72 }
73 }
74
75 pub fn block_height(mut self, height: BlockHeight) -> Self {
79 self.block_ref = Some(BlockId::Height(height).into());
80 self
81 }
82
83 pub fn block_hash(mut self, hash: CryptoHash) -> Self {
87 self.block_ref = Some(BlockId::Hash(near_primitives::hash::CryptoHash(hash.0)).into());
88 self
89 }
90}
91
92impl<T> Query<'_, T>
94where
95 T: ProcessQuery<Method = methods::query::RpcQueryRequest>,
96{
97 pub fn finality(mut self, value: Finality) -> Self {
99 self.block_ref = Some(value.into());
100 self
101 }
102
103 pub(crate) fn block_reference(mut self, value: BlockReference) -> Self {
104 self.block_ref = Some(value);
105 self
106 }
107}
108
109impl<'a, T, R> std::future::IntoFuture for Query<'a, T>
110where
111 T: ProcessQuery<Output = R> + Send + Sync + 'static,
112 <T as ProcessQuery>::Method: RpcMethod + Debug + Send + Sync,
113 <<T as ProcessQuery>::Method as RpcMethod>::Response: Debug + Send + Sync,
114 <<T as ProcessQuery>::Method as RpcMethod>::Error: Debug + Display + Send + Sync,
115{
116 type Output = Result<R>;
117
118 type IntoFuture = BoxFuture<'a, Self::Output>;
121
122 fn into_future(self) -> Self::IntoFuture {
123 Box::pin(async move {
124 let block_reference = self.block_ref.unwrap_or_else(BlockReference::latest);
125 let resp = self
126 .client
127 .query(self.method.into_request(block_reference)?)
128 .await
129 .map_err(|e| RpcErrorCode::QueryFailure.custom(e))?;
130
131 T::from_response(resp)
132 })
133 }
134}
135
136pub trait ProcessQuery {
142 type Method: RpcMethod;
146
147 type Output;
150
151 fn into_request(self, block_ref: BlockReference) -> Result<Self::Method>;
153
154 fn from_response(resp: <Self::Method as RpcMethod>::Response) -> Result<Self::Output>;
157}
158
159pub struct ViewFunction {
160 pub(crate) account_id: AccountId,
161 pub(crate) function: Function,
162}
163
164pub struct ViewCode {
165 pub(crate) account_id: AccountId,
166}
167
168pub struct ViewAccount {
169 pub(crate) account_id: AccountId,
170}
171
172pub struct ViewBlock;
173
174pub struct ViewState {
175 account_id: AccountId,
176 prefix: Option<Vec<u8>>,
177}
178
179pub struct ViewAccessKey {
180 pub(crate) account_id: AccountId,
181 pub(crate) public_key: PublicKey,
182}
183
184pub struct ViewAccessKeyList {
185 pub(crate) account_id: AccountId,
186}
187
188pub struct GasPrice;
189
190impl ProcessQuery for ViewFunction {
191 type Method = methods::query::RpcQueryRequest;
192 type Output = ViewResultDetails;
193
194 fn into_request(self, block_reference: BlockReference) -> Result<Self::Method> {
195 Ok(Self::Method {
196 block_reference,
197 request: QueryRequest::CallFunction {
198 account_id: self.account_id,
199 method_name: self.function.name,
200 args: self.function.args?.into(),
201 },
202 })
203 }
204
205 fn from_response(resp: RpcQueryResponse) -> Result<Self::Output> {
206 match resp.kind {
207 QueryResponseKind::CallResult(result) => Ok(result.into()),
208 _ => Err(RpcErrorCode::QueryReturnedInvalidData.message("while querying account")),
209 }
210 }
211}
212
213impl Query<'_, ViewFunction> {
215 pub fn args(mut self, args: Vec<u8>) -> Self {
219 self.method.function = self.method.function.args(args);
220 self
221 }
222
223 pub fn args_json<U: serde::Serialize>(mut self, args: U) -> Self {
227 self.method.function = self.method.function.args_json(args);
228 self
229 }
230
231 pub fn args_borsh<U: near_primitives::borsh::BorshSerialize>(mut self, args: U) -> Self {
234 self.method.function = self.method.function.args_borsh(args);
235 self
236 }
237}
238
239impl ProcessQuery for ViewCode {
240 type Method = methods::query::RpcQueryRequest;
241 type Output = Vec<u8>;
242
243 fn into_request(self, block_reference: BlockReference) -> Result<Self::Method> {
244 Ok(Self::Method {
245 block_reference,
246 request: QueryRequest::ViewCode {
247 account_id: self.account_id,
248 },
249 })
250 }
251
252 fn from_response(resp: RpcQueryResponse) -> Result<Self::Output> {
253 match resp.kind {
254 QueryResponseKind::ViewCode(contract) => Ok(contract.code),
255 _ => Err(RpcErrorCode::QueryReturnedInvalidData.message("while querying code")),
256 }
257 }
258}
259
260impl ProcessQuery for ViewAccount {
261 type Method = methods::query::RpcQueryRequest;
262 type Output = AccountDetails;
263
264 fn into_request(self, block_reference: BlockReference) -> Result<Self::Method> {
265 Ok(Self::Method {
266 block_reference,
267 request: QueryRequest::ViewAccount {
268 account_id: self.account_id,
269 },
270 })
271 }
272
273 fn from_response(resp: RpcQueryResponse) -> Result<Self::Output> {
274 match resp.kind {
275 QueryResponseKind::ViewAccount(account) => Ok(account.into()),
276 _ => Err(RpcErrorCode::QueryReturnedInvalidData.message("while querying account")),
277 }
278 }
279}
280
281impl ProcessQuery for ViewBlock {
282 type Method = methods::block::RpcBlockRequest;
283 type Output = Block;
284
285 fn into_request(self, block_reference: BlockReference) -> Result<Self::Method> {
286 Ok(Self::Method { block_reference })
287 }
288
289 fn from_response(view: BlockView) -> Result<Self::Output> {
290 Ok(view.into())
291 }
292}
293
294impl ProcessQuery for ViewState {
295 type Method = methods::query::RpcQueryRequest;
296 type Output = HashMap<Vec<u8>, Vec<u8>>;
297
298 fn into_request(self, block_reference: BlockReference) -> Result<Self::Method> {
299 Ok(Self::Method {
300 block_reference,
301 request: QueryRequest::ViewState {
302 account_id: self.account_id,
303 prefix: StoreKey::from(self.prefix.unwrap_or_default()),
304 include_proof: false,
305 },
306 })
307 }
308
309 fn from_response(resp: <Self::Method as RpcMethod>::Response) -> Result<Self::Output> {
310 match resp.kind {
311 QueryResponseKind::ViewState(state) => Ok(tool::into_state_map(state.values)),
312 _ => Err(RpcErrorCode::QueryReturnedInvalidData.message("while querying state")),
313 }
314 }
315}
316
317impl<'a> Query<'a, ViewState> {
318 pub(crate) fn view_state(client: &'a Client, id: &AccountId) -> Self {
319 Self::new(
320 client,
321 ViewState {
322 account_id: id.clone(),
323 prefix: None,
324 },
325 )
326 }
327
328 pub fn prefix(mut self, value: &[u8]) -> Self {
330 self.method.prefix = Some(value.into());
331 self
332 }
333}
334
335impl ProcessQuery for ViewAccessKey {
336 type Method = methods::query::RpcQueryRequest;
337 type Output = AccessKey;
338
339 fn into_request(self, block_reference: BlockReference) -> Result<Self::Method> {
340 Ok(Self::Method {
341 block_reference,
342 request: QueryRequest::ViewAccessKey {
343 account_id: self.account_id,
344 public_key: self.public_key.into(),
345 },
346 })
347 }
348
349 fn from_response(resp: <Self::Method as RpcMethod>::Response) -> Result<Self::Output> {
350 match resp.kind {
351 QueryResponseKind::AccessKey(key) => Ok(key.into()),
352 _ => Err(RpcErrorCode::QueryReturnedInvalidData.message("while querying access key")),
353 }
354 }
355}
356
357impl ProcessQuery for ViewAccessKeyList {
358 type Method = methods::query::RpcQueryRequest;
359 type Output = Vec<AccessKeyInfo>;
360
361 fn into_request(self, block_reference: BlockReference) -> Result<Self::Method> {
362 Ok(Self::Method {
363 block_reference,
364 request: QueryRequest::ViewAccessKeyList {
365 account_id: self.account_id,
366 },
367 })
368 }
369
370 fn from_response(resp: <Self::Method as RpcMethod>::Response) -> Result<Self::Output> {
371 match resp.kind {
372 QueryResponseKind::AccessKeyList(keylist) => {
373 Ok(keylist.keys.into_iter().map(Into::into).collect())
374 }
375 _ => Err(RpcErrorCode::QueryReturnedInvalidData.message("while querying access keys")),
376 }
377 }
378}
379
380impl ProcessQuery for GasPrice {
381 type Method = methods::gas_price::RpcGasPriceRequest;
382 type Output = NearToken;
383
384 fn into_request(self, block_ref: BlockReference) -> Result<Self::Method> {
385 let block_id = match block_ref {
386 BlockReference::BlockId(block_id) => Some(block_id),
388 BlockReference::Finality(_finality) => None,
390 BlockReference::SyncCheckpoint(point) => {
392 return Err(RpcErrorCode::QueryFailure.message(format!(
393 "Cannot supply sync checkpoint to gas price: {point:?}. Potential API bug?"
394 )))
395 }
396 };
397
398 Ok(Self::Method { block_id })
399 }
400
401 fn from_response(resp: <Self::Method as RpcMethod>::Response) -> Result<Self::Output> {
402 Ok(resp.gas_price)
403 }
404}
405
406pub struct QueryChunk<'a> {
413 client: &'a Client,
414 chunk_ref: Option<ChunkReference>,
415}
416
417impl<'a> QueryChunk<'a> {
418 pub(crate) fn new(client: &'a Client) -> Self {
419 Self {
420 client,
421 chunk_ref: None,
422 }
423 }
424
425 pub fn block_hash_and_shard(mut self, hash: CryptoHash, shard_id: ShardId) -> Self {
429 self.chunk_ref = Some(ChunkReference::BlockShardId {
430 block_id: BlockId::Hash(near_primitives::hash::CryptoHash(hash.0)),
431 shard_id: shard_id.into(),
432 });
433 self
434 }
435
436 pub fn block_height_and_shard(mut self, height: BlockHeight, shard_id: ShardId) -> Self {
440 self.chunk_ref = Some(ChunkReference::BlockShardId {
441 block_id: BlockId::Height(height),
442 shard_id: shard_id.into(),
443 });
444 self
445 }
446
447 pub fn chunk_hash(mut self, hash: CryptoHash) -> Self {
449 self.chunk_ref = Some(ChunkReference::ChunkHash {
450 chunk_id: near_primitives::hash::CryptoHash(hash.0),
451 });
452 self
453 }
454}
455
456impl<'a> std::future::IntoFuture for QueryChunk<'a> {
457 type Output = Result<Chunk>;
458 type IntoFuture = BoxFuture<'a, Self::Output>;
459
460 fn into_future(self) -> Self::IntoFuture {
461 Box::pin(async move {
462 let chunk_reference = if let Some(chunk_ref) = self.chunk_ref {
463 chunk_ref
464 } else {
465 let block_view = self.client.view_block(None).await?;
468 ChunkReference::BlockShardId {
469 block_id: BlockId::Hash(block_view.header.hash),
470 shard_id: 0.into(),
471 }
472 };
473
474 let chunk_view = self
475 .client
476 .query(methods::chunk::RpcChunkRequest { chunk_reference })
477 .await
478 .map_err(|e| RpcErrorCode::QueryFailure.custom(e))?;
479
480 Ok(chunk_view.into())
481 })
482 }
483}