Skip to main content

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