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