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