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