iota_sdk/apis/
read.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{collections::BTreeMap, sync::Arc};
6
7use fastcrypto::encoding::Base64;
8use futures::{StreamExt, stream};
9use futures_core::Stream;
10use iota_json_rpc_api::{
11    GovernanceReadApiClient, IndexerApiClient, MoveUtilsClient, ReadApiClient, WriteApiClient,
12};
13use iota_json_rpc_types::{
14    Checkpoint, CheckpointId, CheckpointPage, DevInspectArgs, DevInspectResults,
15    DryRunTransactionBlockResponse, DynamicFieldPage, IotaData, IotaGetPastObjectRequest,
16    IotaMoveNormalizedModule, IotaObjectDataOptions, IotaObjectResponse, IotaObjectResponseQuery,
17    IotaPastObjectResponse, IotaTransactionBlockEffects, IotaTransactionBlockResponse,
18    IotaTransactionBlockResponseOptions, IotaTransactionBlockResponseQuery, ObjectsPage,
19    ProtocolConfigResponse, TransactionBlocksPage, TransactionFilter,
20};
21use iota_types::{
22    base_types::{IotaAddress, ObjectID, SequenceNumber, TransactionDigest},
23    dynamic_field::DynamicFieldName,
24    iota_serde::BigInt,
25    messages_checkpoint::CheckpointSequenceNumber,
26    transaction::{TransactionData, TransactionKind},
27};
28use jsonrpsee::core::client::Subscription;
29
30use crate::{
31    RpcClient,
32    error::{Error, IotaRpcResult},
33};
34
35/// Defines methods for retrieving data about objects and transactions.
36#[derive(Debug)]
37pub struct ReadApi {
38    api: Arc<RpcClient>,
39}
40
41impl ReadApi {
42    pub(crate) fn new(api: Arc<RpcClient>) -> Self {
43        Self { api }
44    }
45
46    /// Get the objects owned by the given address.
47    /// Results are paginated.
48    ///
49    /// Note that if the address owns more than
50    /// [`QUERY_MAX_RESULT_LIMIT`](iota_json_rpc_api::QUERY_MAX_RESULT_LIMIT)
51    /// objects (default is 50), the pagination may not be accurate as the
52    /// previous page may have been updated before the next page is fetched.
53    ///
54    /// # Examples
55    ///
56    /// ```rust,no_run
57    /// use std::str::FromStr;
58    ///
59    /// use iota_sdk::IotaClientBuilder;
60    /// use iota_types::base_types::IotaAddress;
61    ///
62    /// #[tokio::main]
63    /// async fn main() -> Result<(), anyhow::Error> {
64    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
65    ///     let address = IotaAddress::from_str("0x0000....0000")?;
66    ///     let owned_objects = iota
67    ///         .read_api()
68    ///         .get_owned_objects(address, None, None, None)
69    ///         .await?;
70    ///     Ok(())
71    /// }
72    /// ```
73    pub async fn get_owned_objects(
74        &self,
75        address: IotaAddress,
76        query: impl Into<Option<IotaObjectResponseQuery>>,
77        cursor: impl Into<Option<ObjectID>>,
78        limit: impl Into<Option<usize>>,
79    ) -> IotaRpcResult<ObjectsPage> {
80        Ok(self
81            .api
82            .http
83            .get_owned_objects(address, query.into(), cursor.into(), limit.into())
84            .await?)
85    }
86
87    /// Get the dynamic fields owned by the given [ObjectID].
88    /// Results are paginated.
89    ///
90    /// If the field is a dynamic field, this method returns the ID of the Field
91    /// object, which contains both the name and the value.
92    ///
93    /// If the field is a dynamic object field, it returns the ID of the Object,
94    /// which is the value of the field.
95    ///
96    /// # Examples
97    ///
98    /// ```rust,no_run
99    /// use std::str::FromStr;
100    ///
101    /// use iota_sdk::IotaClientBuilder;
102    /// use iota_types::base_types::{IotaAddress, ObjectID};
103    ///
104    /// #[tokio::main]
105    /// async fn main() -> Result<(), anyhow::Error> {
106    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
107    ///     let address = IotaAddress::from_str("0x0000....0000")?;
108    ///     let owned_objects = iota
109    ///         .read_api()
110    ///         .get_owned_objects(address, None, None, None)
111    ///         .await?;
112    ///     // this code example assumes that there are previous owned objects
113    ///     let object = owned_objects
114    ///         .data
115    ///         .get(0)
116    ///         .expect(&format!("No owned objects for this address {}", address));
117    ///     let object_data = object.data.as_ref().expect(&format!(
118    ///         "No object data for this IotaObjectResponse {:?}",
119    ///         object
120    ///     ));
121    ///     let object_id = object_data.object_id;
122    ///     let dynamic_fields = iota
123    ///         .read_api()
124    ///         .get_dynamic_fields(object_id, None, None)
125    ///         .await?;
126    ///     Ok(())
127    /// }
128    /// ```
129    pub async fn get_dynamic_fields(
130        &self,
131        object_id: ObjectID,
132        cursor: impl Into<Option<ObjectID>>,
133        limit: impl Into<Option<usize>>,
134    ) -> IotaRpcResult<DynamicFieldPage> {
135        Ok(self
136            .api
137            .http
138            .get_dynamic_fields(object_id, cursor.into(), limit.into())
139            .await?)
140    }
141
142    /// Get information for a specified dynamic field object by its parent
143    /// object ID and field name.
144    pub async fn get_dynamic_field_object(
145        &self,
146        parent_object_id: ObjectID,
147        name: DynamicFieldName,
148    ) -> IotaRpcResult<IotaObjectResponse> {
149        Ok(self
150            .api
151            .http
152            .get_dynamic_field_object(parent_object_id, name)
153            .await?)
154    }
155
156    /// Get information for a specified dynamic field object by its parent
157    /// object ID and field name with options.
158    pub async fn get_dynamic_field_object_v2(
159        &self,
160        parent_object_id: ObjectID,
161        name: DynamicFieldName,
162        options: impl Into<Option<IotaObjectDataOptions>>,
163    ) -> IotaRpcResult<IotaObjectResponse> {
164        Ok(self
165            .api
166            .http
167            .get_dynamic_field_object_v2(parent_object_id, name, options.into())
168            .await?)
169    }
170
171    /// Get a parsed past object and version for the provided object ID.
172    ///
173    /// An object's version increases when the object is mutated, though it is
174    /// not guaranteed that it increases always by 1. A past object can be used
175    /// to understand how the object changed over time, i.e. what was the total
176    /// balance at a specific version.
177    ///
178    /// # Examples
179    ///
180    /// ```rust,no_run
181    /// use std::str::FromStr;
182    ///
183    /// use iota_json_rpc_types::IotaObjectDataOptions;
184    /// use iota_sdk::IotaClientBuilder;
185    /// use iota_types::base_types::{IotaAddress, ObjectID};
186    ///
187    /// #[tokio::main]
188    /// async fn main() -> Result<(), anyhow::Error> {
189    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
190    ///     let address = IotaAddress::from_str("0x0000....0000")?;
191    ///     let owned_objects = iota
192    ///         .read_api()
193    ///         .get_owned_objects(address, None, None, None)
194    ///         .await?;
195    ///     // this code example assumes that there are previous owned objects
196    ///     let object = owned_objects
197    ///         .data
198    ///         .get(0)
199    ///         .expect(&format!("No owned objects for this address {}", address));
200    ///     let object_data = object.data.as_ref().expect(&format!(
201    ///         "No object data for this IotaObjectResponse {:?}",
202    ///         object
203    ///     ));
204    ///     let object_id = object_data.object_id;
205    ///     let version = object_data.version;
206    ///     let past_object = iota
207    ///         .read_api()
208    ///         .try_get_parsed_past_object(
209    ///             object_id,
210    ///             version,
211    ///             IotaObjectDataOptions {
212    ///                 show_type: true,
213    ///                 show_owner: true,
214    ///                 show_previous_transaction: true,
215    ///                 show_display: true,
216    ///                 show_content: true,
217    ///                 show_bcs: true,
218    ///                 show_storage_rebate: true,
219    ///             },
220    ///         )
221    ///         .await?;
222    ///     Ok(())
223    /// }
224    /// ```
225    pub async fn try_get_parsed_past_object(
226        &self,
227        object_id: ObjectID,
228        version: SequenceNumber,
229        options: IotaObjectDataOptions,
230    ) -> IotaRpcResult<IotaPastObjectResponse> {
231        Ok(self
232            .api
233            .http
234            .try_get_past_object(object_id, version, Some(options))
235            .await?)
236    }
237
238    /// Get a list of parsed past objects.
239    ///
240    /// See [Self::try_get_parsed_past_object] for more details about past
241    /// objects.
242    ///
243    /// # Examples
244    ///
245    /// ```rust,no_run
246    /// use std::str::FromStr;
247    ///
248    /// use iota_json_rpc_types::{IotaGetPastObjectRequest, IotaObjectDataOptions};
249    /// use iota_sdk::IotaClientBuilder;
250    /// use iota_types::base_types::{IotaAddress, ObjectID};
251    ///
252    /// #[tokio::main]
253    /// async fn main() -> Result<(), anyhow::Error> {
254    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
255    ///     let address = IotaAddress::from_str("0x0000....0000")?;
256    ///     let owned_objects = iota
257    ///         .read_api()
258    ///         .get_owned_objects(address, None, None, None)
259    ///         .await?;
260    ///     // this code example assumes that there are previous owned objects
261    ///     let object = owned_objects
262    ///         .data
263    ///         .get(0)
264    ///         .expect(&format!("No owned objects for this address {}", address));
265    ///     let object_data = object.data.as_ref().expect(&format!(
266    ///         "No object data for this IotaObjectResponse {:?}",
267    ///         object
268    ///     ));
269    ///     let object_id = object_data.object_id;
270    ///     let version = object_data.version;
271    ///     let past_object = iota
272    ///         .read_api()
273    ///         .try_get_parsed_past_object(
274    ///             object_id,
275    ///             version,
276    ///             IotaObjectDataOptions {
277    ///                 show_type: true,
278    ///                 show_owner: true,
279    ///                 show_previous_transaction: true,
280    ///                 show_display: true,
281    ///                 show_content: true,
282    ///                 show_bcs: true,
283    ///                 show_storage_rebate: true,
284    ///             },
285    ///         )
286    ///         .await?;
287    ///     let past_object = past_object.into_object()?;
288    ///     let multi_past_object = iota
289    ///         .read_api()
290    ///         .try_multi_get_parsed_past_object(
291    ///             vec![IotaGetPastObjectRequest {
292    ///                 object_id: past_object.object_id,
293    ///                 version: past_object.version,
294    ///             }],
295    ///             IotaObjectDataOptions {
296    ///                 show_type: true,
297    ///                 show_owner: true,
298    ///                 show_previous_transaction: true,
299    ///                 show_display: true,
300    ///                 show_content: true,
301    ///                 show_bcs: true,
302    ///                 show_storage_rebate: true,
303    ///             },
304    ///         )
305    ///         .await?;
306    ///     Ok(())
307    /// }
308    /// ```
309    pub async fn try_multi_get_parsed_past_object(
310        &self,
311        past_objects: Vec<IotaGetPastObjectRequest>,
312        options: IotaObjectDataOptions,
313    ) -> IotaRpcResult<Vec<IotaPastObjectResponse>> {
314        Ok(self
315            .api
316            .http
317            .try_multi_get_past_objects(past_objects, Some(options))
318            .await?)
319    }
320
321    /// Get an object by object ID with optional fields enabled by
322    /// [IotaObjectDataOptions].
323    ///
324    /// # Examples
325    ///
326    /// ```rust,no_run
327    /// use std::str::FromStr;
328    ///
329    /// use iota_json_rpc_types::IotaObjectDataOptions;
330    /// use iota_sdk::IotaClientBuilder;
331    /// use iota_types::base_types::IotaAddress;
332    ///
333    /// #[tokio::main]
334    /// async fn main() -> Result<(), anyhow::Error> {
335    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
336    ///     let address = IotaAddress::from_str("0x0000....0000")?;
337    ///     let owned_objects = iota
338    ///         .read_api()
339    ///         .get_owned_objects(address, None, None, None)
340    ///         .await?;
341    ///     // this code example assumes that there are previous owned objects
342    ///     let object = owned_objects
343    ///         .data
344    ///         .get(0)
345    ///         .expect(&format!("No owned objects for this address {}", address));
346    ///     let object_data = object.data.as_ref().expect(&format!(
347    ///         "No object data for this IotaObjectResponse {:?}",
348    ///         object
349    ///     ));
350    ///     let object_id = object_data.object_id;
351    ///     let object = iota
352    ///         .read_api()
353    ///         .get_object_with_options(
354    ///             object_id,
355    ///             IotaObjectDataOptions {
356    ///                 show_type: true,
357    ///                 show_owner: true,
358    ///                 show_previous_transaction: true,
359    ///                 show_display: true,
360    ///                 show_content: true,
361    ///                 show_bcs: true,
362    ///                 show_storage_rebate: true,
363    ///             },
364    ///         )
365    ///         .await?;
366    ///     Ok(())
367    /// }
368    /// ```
369    pub async fn get_object_with_options(
370        &self,
371        object_id: ObjectID,
372        options: IotaObjectDataOptions,
373    ) -> IotaRpcResult<IotaObjectResponse> {
374        Ok(self.api.http.get_object(object_id, Some(options)).await?)
375    }
376
377    /// Get a list of objects by their object IDs with optional fields enabled
378    /// by [IotaObjectDataOptions].
379    ///
380    /// # Examples
381    ///
382    /// ```rust,no_run
383    /// use std::str::FromStr;
384    ///
385    /// use iota_json_rpc_types::IotaObjectDataOptions;
386    /// use iota_sdk::IotaClientBuilder;
387    /// use iota_types::base_types::IotaAddress;
388    /// #[tokio::main]
389    /// async fn main() -> Result<(), anyhow::Error> {
390    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
391    ///     let address = IotaAddress::from_str("0x0000....0000")?;
392    ///     let owned_objects = iota
393    ///         .read_api()
394    ///         .get_owned_objects(address, None, None, None)
395    ///         .await?;
396    ///     // this code example assumes that there are previous owned objects
397    ///     let object = owned_objects
398    ///         .data
399    ///         .get(0)
400    ///         .expect(&format!("No owned objects for this address {}", address));
401    ///     let object_data = object.data.as_ref().expect(&format!(
402    ///         "No object data for this IotaObjectResponse {:?}",
403    ///         object
404    ///     ));
405    ///     let object_id = object_data.object_id;
406    ///     let object_ids = vec![object_id]; // and other object ids
407    ///     let object = iota
408    ///         .read_api()
409    ///         .multi_get_object_with_options(
410    ///             object_ids,
411    ///             IotaObjectDataOptions {
412    ///                 show_type: true,
413    ///                 show_owner: true,
414    ///                 show_previous_transaction: true,
415    ///                 show_display: true,
416    ///                 show_content: true,
417    ///                 show_bcs: true,
418    ///                 show_storage_rebate: true,
419    ///             },
420    ///         )
421    ///         .await?;
422    ///     Ok(())
423    /// }
424    /// ```
425    pub async fn multi_get_object_with_options(
426        &self,
427        object_ids: Vec<ObjectID>,
428        options: IotaObjectDataOptions,
429    ) -> IotaRpcResult<Vec<IotaObjectResponse>> {
430        Ok(self
431            .api
432            .http
433            .multi_get_objects(object_ids, Some(options))
434            .await?)
435    }
436
437    /// Get a [bcs] serialized object's bytes by object ID.
438    pub async fn get_move_object_bcs(&self, object_id: ObjectID) -> IotaRpcResult<Vec<u8>> {
439        let resp = self
440            .get_object_with_options(object_id, IotaObjectDataOptions::default().with_bcs())
441            .await?
442            .into_object()
443            .map_err(|e| {
444                Error::Data(format!("Can't get bcs of object {:?}: {:?}", object_id, e))
445            })?;
446        // unwrap: requested bcs data
447        let move_object = resp.bcs.unwrap();
448        let raw_move_obj = move_object.try_into_move().ok_or(Error::Data(format!(
449            "Object {:?} is not a MoveObject",
450            object_id
451        )))?;
452        Ok(raw_move_obj.bcs_bytes)
453    }
454
455    /// Get the total number of transaction blocks known to server.
456    ///
457    /// # Examples
458    ///
459    /// ```rust,no_run
460    /// use iota_sdk::IotaClientBuilder;
461    ///
462    /// #[tokio::main]
463    /// async fn main() -> Result<(), anyhow::Error> {
464    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
465    ///     let total_transaction_blocks = iota.read_api().get_total_transaction_blocks().await?;
466    ///     Ok(())
467    /// }
468    /// ```
469    pub async fn get_total_transaction_blocks(&self) -> IotaRpcResult<u64> {
470        Ok(*self.api.http.get_total_transaction_blocks().await?)
471    }
472
473    /// Get a transaction and its effects by its digest with optional fields
474    /// enabled by [IotaTransactionBlockResponseOptions].
475    pub async fn get_transaction_with_options(
476        &self,
477        digest: TransactionDigest,
478        options: IotaTransactionBlockResponseOptions,
479    ) -> IotaRpcResult<IotaTransactionBlockResponse> {
480        Ok(self
481            .api
482            .http
483            .get_transaction_block(digest, Some(options))
484            .await?)
485    }
486
487    /// Get a list of transactions and their effects by their digests with
488    /// optional fields enabled by [IotaTransactionBlockResponseOptions].
489    pub async fn multi_get_transactions_with_options(
490        &self,
491        digests: Vec<TransactionDigest>,
492        options: IotaTransactionBlockResponseOptions,
493    ) -> IotaRpcResult<Vec<IotaTransactionBlockResponse>> {
494        Ok(self
495            .api
496            .http
497            .multi_get_transaction_blocks(digests, Some(options))
498            .await?)
499    }
500
501    /// Get filtered transaction blocks information.
502    /// Results are paginated.
503    pub async fn query_transaction_blocks(
504        &self,
505        query: IotaTransactionBlockResponseQuery,
506        cursor: impl Into<Option<TransactionDigest>>,
507        limit: impl Into<Option<usize>>,
508        descending_order: bool,
509    ) -> IotaRpcResult<TransactionBlocksPage> {
510        Ok(self
511            .api
512            .http
513            .query_transaction_blocks(query, cursor.into(), limit.into(), Some(descending_order))
514            .await?)
515    }
516
517    /// Get the first four bytes of the chain's genesis checkpoint digest in hex
518    /// format.
519    pub async fn get_chain_identifier(&self) -> IotaRpcResult<String> {
520        Ok(self.api.http.get_chain_identifier().await?)
521    }
522
523    /// Get a checkpoint by its ID.
524    pub async fn get_checkpoint(&self, id: CheckpointId) -> IotaRpcResult<Checkpoint> {
525        Ok(self.api.http.get_checkpoint(id).await?)
526    }
527
528    /// Return a list of checkpoints.
529    /// Results are paginated.
530    pub async fn get_checkpoints(
531        &self,
532        cursor: impl Into<Option<BigInt<u64>>>,
533        limit: impl Into<Option<usize>>,
534        descending_order: bool,
535    ) -> IotaRpcResult<CheckpointPage> {
536        Ok(self
537            .api
538            .http
539            .get_checkpoints(cursor.into(), limit.into(), descending_order)
540            .await?)
541    }
542
543    /// Get the sequence number of the latest checkpoint that has been executed.
544    pub async fn get_latest_checkpoint_sequence_number(
545        &self,
546    ) -> IotaRpcResult<CheckpointSequenceNumber> {
547        Ok(*self
548            .api
549            .http
550            .get_latest_checkpoint_sequence_number()
551            .await?)
552    }
553
554    /// Get a stream of transactions.
555    pub fn get_transactions_stream(
556        &self,
557        query: IotaTransactionBlockResponseQuery,
558        cursor: impl Into<Option<TransactionDigest>>,
559        descending_order: bool,
560    ) -> impl Stream<Item = IotaTransactionBlockResponse> + '_ {
561        let cursor = cursor.into();
562
563        stream::unfold(
564            (vec![], cursor, true, query),
565            move |(mut data, cursor, first, query)| async move {
566                if let Some(item) = data.pop() {
567                    Some((item, (data, cursor, false, query)))
568                } else if (cursor.is_none() && first) || cursor.is_some() {
569                    let page = self
570                        .query_transaction_blocks(
571                            query.clone(),
572                            cursor,
573                            Some(100),
574                            descending_order,
575                        )
576                        .await
577                        .ok()?;
578                    let mut data = page.data;
579                    data.reverse();
580                    data.pop()
581                        .map(|item| (item, (data, page.next_cursor, false, query)))
582                } else {
583                    None
584                }
585            },
586        )
587    }
588
589    /// Subscribe to a stream of transactions.
590    ///
591    /// This is only available through WebSockets.
592    pub async fn subscribe_transaction(
593        &self,
594        filter: TransactionFilter,
595    ) -> IotaRpcResult<impl Stream<Item = IotaRpcResult<IotaTransactionBlockEffects>>> {
596        let Some(c) = &self.api.ws else {
597            return Err(Error::Subscription(
598                "Subscription only supported by WebSocket client.".to_string(),
599            ));
600        };
601        let subscription: Subscription<IotaTransactionBlockEffects> =
602            c.subscribe_transaction(filter).await?;
603        Ok(subscription.map(|item| Ok(item?)))
604    }
605
606    /// Get move modules by package ID, keyed by name.
607    pub async fn get_normalized_move_modules_by_package(
608        &self,
609        package: ObjectID,
610    ) -> IotaRpcResult<BTreeMap<String, IotaMoveNormalizedModule>> {
611        Ok(self
612            .api
613            .http
614            .get_normalized_move_modules_by_package(package)
615            .await?)
616    }
617
618    // TODO(devx): we can probably cache this given an epoch
619    /// Get the reference gas price.
620    pub async fn get_reference_gas_price(&self) -> IotaRpcResult<u64> {
621        Ok(*self.api.http.get_reference_gas_price().await?)
622    }
623
624    /// Dry run a transaction block given the provided transaction data.
625    ///
626    /// This simulates running the transaction, including all standard checks,
627    /// without actually running it. This is useful for estimating the gas fees
628    /// of a transaction before executing it. You can also use it to identify
629    /// any side-effects of a transaction before you execute it on the network.
630    pub async fn dry_run_transaction_block(
631        &self,
632        tx: TransactionData,
633    ) -> IotaRpcResult<DryRunTransactionBlockResponse> {
634        Ok(self
635            .api
636            .http
637            .dry_run_transaction_block(Base64::from_bytes(&bcs::to_bytes(&tx)?))
638            .await?)
639    }
640
641    /// Use this function to inspect the current state of the network by running
642    /// a programmable transaction block without committing its effects on
643    /// chain.
644    ///
645    /// Unlike a dry run, this method will not validate whether the transaction
646    /// block would succeed or fail under normal circumstances, e.g.:
647    ///
648    /// - Transaction inputs are not checked for ownership (i.e. you can
649    ///   construct calls involving objects you do not own)
650    /// - Calls are not checked for visibility (you can call private functions
651    ///   on modules)
652    /// - Inputs of any type can be constructed and passed in, including coins
653    ///   and other objects that would usually need to be constructed with a
654    ///   move call
655    /// - Function returns do not need to be used, even if they do not have
656    ///   `drop`
657    ///
658    /// This method's output includes a breakdown of results returned by every
659    /// transaction in the block, as well as the transaction's effects.
660    ///
661    /// To run an accurate simulation of a transaction and understand whether
662    /// it will successfully validate and run, use
663    /// [Self::dry_run_transaction_block] instead.
664    pub async fn dev_inspect_transaction_block(
665        &self,
666        sender_address: IotaAddress,
667        tx: TransactionKind,
668        gas_price: impl Into<Option<BigInt<u64>>>,
669        epoch: impl Into<Option<BigInt<u64>>>,
670        additional_args: impl Into<Option<DevInspectArgs>>,
671    ) -> IotaRpcResult<DevInspectResults> {
672        Ok(self
673            .api
674            .http
675            .dev_inspect_transaction_block(
676                sender_address,
677                Base64::from_bytes(&bcs::to_bytes(&tx)?),
678                gas_price.into(),
679                epoch.into(),
680                additional_args.into(),
681            )
682            .await?)
683    }
684
685    /// Get the protocol config by version.
686    ///
687    /// The version defaults to the current version.
688    pub async fn get_protocol_config(
689        &self,
690        version: impl Into<Option<BigInt<u64>>>,
691    ) -> IotaRpcResult<ProtocolConfigResponse> {
692        Ok(self.api.http.get_protocol_config(version.into()).await?)
693    }
694
695    /// Get an object by ID before the given version.
696    pub async fn try_get_object_before_version(
697        &self,
698        object_id: ObjectID,
699        version: SequenceNumber,
700    ) -> IotaRpcResult<IotaPastObjectResponse> {
701        Ok(self
702            .api
703            .http
704            .try_get_object_before_version(object_id, version)
705            .await?)
706    }
707}