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| Error::Data(format!("Can't get bcs of object {object_id:?}: {e:?}")))?;
452        // unwrap: requested bcs data
453        let move_object = resp.bcs.unwrap();
454        let raw_move_obj = move_object.try_into_move().ok_or(Error::Data(format!(
455            "Object {object_id:?} is not a MoveObject"
456        )))?;
457        Ok(raw_move_obj.bcs_bytes)
458    }
459
460    /// Get the total number of transaction blocks known to server.
461    ///
462    /// # Examples
463    ///
464    /// ```rust,no_run
465    /// use iota_sdk::IotaClientBuilder;
466    ///
467    /// #[tokio::main]
468    /// async fn main() -> Result<(), anyhow::Error> {
469    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
470    ///     let total_transaction_blocks = iota.read_api().get_total_transaction_blocks().await?;
471    ///     Ok(())
472    /// }
473    /// ```
474    pub async fn get_total_transaction_blocks(&self) -> IotaRpcResult<u64> {
475        Ok(*self.api.http.get_total_transaction_blocks().await?)
476    }
477
478    /// Get a transaction and its effects by its digest with optional fields
479    /// enabled by [IotaTransactionBlockResponseOptions].
480    pub async fn get_transaction_with_options(
481        &self,
482        digest: TransactionDigest,
483        options: IotaTransactionBlockResponseOptions,
484    ) -> IotaRpcResult<IotaTransactionBlockResponse> {
485        Ok(self
486            .api
487            .http
488            .get_transaction_block(digest, Some(options))
489            .await?)
490    }
491
492    /// Get a list of transactions and their effects by their digests with
493    /// optional fields enabled by [IotaTransactionBlockResponseOptions].
494    pub async fn multi_get_transactions_with_options(
495        &self,
496        digests: Vec<TransactionDigest>,
497        options: IotaTransactionBlockResponseOptions,
498    ) -> IotaRpcResult<Vec<IotaTransactionBlockResponse>> {
499        Ok(self
500            .api
501            .http
502            .multi_get_transaction_blocks(digests, Some(options))
503            .await?)
504    }
505
506    /// Get filtered transaction blocks information.
507    /// Results are paginated.
508    pub async fn query_transaction_blocks(
509        &self,
510        query: IotaTransactionBlockResponseQuery,
511        cursor: impl Into<Option<TransactionDigest>>,
512        limit: impl Into<Option<usize>>,
513        descending_order: bool,
514    ) -> IotaRpcResult<TransactionBlocksPage> {
515        Ok(self
516            .api
517            .http
518            .query_transaction_blocks(query, cursor.into(), limit.into(), Some(descending_order))
519            .await?)
520    }
521
522    /// Get the first four bytes of the chain's genesis checkpoint digest in hex
523    /// format.
524    pub async fn get_chain_identifier(&self) -> IotaRpcResult<String> {
525        Ok(self.api.http.get_chain_identifier().await?)
526    }
527
528    /// Get a checkpoint by its ID.
529    pub async fn get_checkpoint(&self, id: CheckpointId) -> IotaRpcResult<Checkpoint> {
530        Ok(self.api.http.get_checkpoint(id).await?)
531    }
532
533    /// Return a list of checkpoints.
534    /// Results are paginated.
535    pub async fn get_checkpoints(
536        &self,
537        cursor: impl Into<Option<BigInt<u64>>>,
538        limit: impl Into<Option<usize>>,
539        descending_order: bool,
540    ) -> IotaRpcResult<CheckpointPage> {
541        Ok(self
542            .api
543            .http
544            .get_checkpoints(cursor.into(), limit.into(), descending_order)
545            .await?)
546    }
547
548    /// Get the sequence number of the latest checkpoint that has been executed.
549    pub async fn get_latest_checkpoint_sequence_number(
550        &self,
551    ) -> IotaRpcResult<CheckpointSequenceNumber> {
552        Ok(*self
553            .api
554            .http
555            .get_latest_checkpoint_sequence_number()
556            .await?)
557    }
558
559    /// Get a stream of transactions.
560    pub fn get_transactions_stream(
561        &self,
562        query: IotaTransactionBlockResponseQuery,
563        cursor: impl Into<Option<TransactionDigest>>,
564        descending_order: bool,
565    ) -> impl Stream<Item = IotaTransactionBlockResponse> + '_ {
566        let cursor = cursor.into();
567
568        stream::unfold(
569            (vec![], cursor, true, query),
570            move |(mut data, cursor, first, query)| async move {
571                if let Some(item) = data.pop() {
572                    Some((item, (data, cursor, false, query)))
573                } else if (cursor.is_none() && first) || cursor.is_some() {
574                    let page = self
575                        .query_transaction_blocks(
576                            query.clone(),
577                            cursor,
578                            Some(100),
579                            descending_order,
580                        )
581                        .await
582                        .ok()?;
583                    let mut data = page.data;
584                    data.reverse();
585                    data.pop()
586                        .map(|item| (item, (data, page.next_cursor, false, query)))
587                } else {
588                    None
589                }
590            },
591        )
592    }
593
594    /// Subscribe to a stream of transactions.
595    ///
596    /// This is only available through WebSockets.
597    pub async fn subscribe_transaction(
598        &self,
599        filter: TransactionFilter,
600    ) -> IotaRpcResult<impl Stream<Item = IotaRpcResult<IotaTransactionBlockEffects>>> {
601        let Some(c) = &self.api.ws else {
602            return Err(Error::Subscription(
603                "Subscription only supported by WebSocket client.".to_string(),
604            ));
605        };
606        let subscription: Subscription<IotaTransactionBlockEffects> =
607            c.subscribe_transaction(filter).await?;
608        Ok(subscription.map(|item| Ok(item?)))
609    }
610
611    /// Get move modules by package ID, keyed by name.
612    pub async fn get_normalized_move_modules_by_package(
613        &self,
614        package: ObjectID,
615    ) -> IotaRpcResult<BTreeMap<String, IotaMoveNormalizedModule>> {
616        Ok(self
617            .api
618            .http
619            .get_normalized_move_modules_by_package(package)
620            .await?)
621    }
622
623    // TODO(devx): we can probably cache this given an epoch
624    /// Get the reference gas price.
625    pub async fn get_reference_gas_price(&self) -> IotaRpcResult<u64> {
626        Ok(*self.api.http.get_reference_gas_price().await?)
627    }
628
629    /// Dry run a transaction block given the provided transaction data.
630    ///
631    /// This simulates running the transaction, including all standard checks,
632    /// without actually running it. This is useful for estimating the gas fees
633    /// of a transaction before executing it. You can also use it to identify
634    /// any side-effects of a transaction before you execute it on the network.
635    pub async fn dry_run_transaction_block(
636        &self,
637        tx: TransactionData,
638    ) -> IotaRpcResult<DryRunTransactionBlockResponse> {
639        Ok(self
640            .api
641            .http
642            .dry_run_transaction_block(Base64::from_bytes(&bcs::to_bytes(&tx)?))
643            .await?)
644    }
645
646    /// Use this function to inspect the current state of the network by running
647    /// a programmable transaction block without committing its effects on
648    /// chain.
649    ///
650    /// Unlike a dry run, this method will not validate whether the transaction
651    /// block would succeed or fail under normal circumstances, e.g.:
652    ///
653    /// - Transaction inputs are not checked for ownership (i.e. you can
654    ///   construct calls involving objects you do not own)
655    /// - Calls are not checked for visibility (you can call private functions
656    ///   on modules)
657    /// - Inputs of any type can be constructed and passed in, including coins
658    ///   and other objects that would usually need to be constructed with a
659    ///   move call
660    /// - Function returns do not need to be used, even if they do not have
661    ///   `drop`
662    ///
663    /// This method's output includes a breakdown of results returned by every
664    /// transaction in the block, as well as the transaction's effects.
665    ///
666    /// To run an accurate simulation of a transaction and understand whether
667    /// it will successfully validate and run, use
668    /// [Self::dry_run_transaction_block] instead.
669    pub async fn dev_inspect_transaction_block(
670        &self,
671        sender_address: IotaAddress,
672        tx: TransactionKind,
673        gas_price: impl Into<Option<BigInt<u64>>>,
674        epoch: impl Into<Option<BigInt<u64>>>,
675        additional_args: impl Into<Option<DevInspectArgs>>,
676    ) -> IotaRpcResult<DevInspectResults> {
677        Ok(self
678            .api
679            .http
680            .dev_inspect_transaction_block(
681                sender_address,
682                Base64::from_bytes(&bcs::to_bytes(&tx)?),
683                gas_price.into(),
684                epoch.into(),
685                additional_args.into(),
686            )
687            .await?)
688    }
689
690    /// Get the protocol config by version.
691    ///
692    /// The version defaults to the current version.
693    pub async fn get_protocol_config(
694        &self,
695        version: impl Into<Option<BigInt<u64>>>,
696    ) -> IotaRpcResult<ProtocolConfigResponse> {
697        Ok(self.api.http.get_protocol_config(version.into()).await?)
698    }
699
700    /// Get an object by ID before the given version.
701    pub async fn try_get_object_before_version(
702        &self,
703        object_id: ObjectID,
704        version: SequenceNumber,
705    ) -> IotaRpcResult<IotaPastObjectResponse> {
706        Ok(self
707            .api
708            .http
709            .try_get_object_before_version(object_id, version)
710            .await?)
711    }
712
713    #[cfg(feature = "iota-names")]
714    /// Return the resolved record for the given name.
715    pub async fn iota_names_lookup(&self, name: &str) -> IotaRpcResult<Option<IotaNameRecord>> {
716        Ok(self.api.http.iota_names_lookup(name).await?)
717    }
718
719    #[cfg(feature = "iota-names")]
720    /// Return the resolved name for the given address.
721    pub async fn iota_names_reverse_lookup(
722        &self,
723        address: IotaAddress,
724    ) -> IotaRpcResult<Option<String>> {
725        Ok(self.api.http.iota_names_reverse_lookup(address).await?)
726    }
727
728    #[cfg(feature = "iota-names")]
729    /// Find all registration NFTs for the given address.
730    pub async fn iota_names_find_all_registration_nfts(
731        &self,
732        address: IotaAddress,
733        cursor: Option<ObjectID>,
734        limit: Option<usize>,
735        options: Option<IotaObjectDataOptions>,
736    ) -> IotaRpcResult<ObjectsPage> {
737        Ok(self
738            .api
739            .http
740            .iota_names_find_all_registration_nfts(address, cursor, limit, options)
741            .await?)
742    }
743}