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::{ObjectId, TransactionKind};
25use iota_types::{
26    base_types::{IotaAddress, 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, types::base_types::IotaAddress};
64    ///
65    /// #[tokio::main]
66    /// async fn main() -> Result<(), anyhow::Error> {
67    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
68    ///     let address = IotaAddress::from_str("0x0000....0000")?;
69    ///     let owned_objects = iota
70    ///         .read_api()
71    ///         .get_owned_objects(address, None, None, None)
72    ///         .await?;
73    ///     Ok(())
74    /// }
75    /// ```
76    pub async fn get_owned_objects(
77        &self,
78        address: IotaAddress,
79        query: impl Into<Option<IotaObjectResponseQuery>>,
80        cursor: impl Into<Option<ObjectId>>,
81        limit: impl Into<Option<usize>>,
82    ) -> IotaRpcResult<ObjectsPage> {
83        Ok(self
84            .api
85            .http
86            .get_owned_objects(address, query.into(), cursor.into(), limit.into())
87            .await?)
88    }
89
90    /// Get the dynamic fields owned by the given [ObjectId].
91    /// Results are paginated.
92    ///
93    /// If the field is a dynamic field, this method returns the ID of the Field
94    /// object, which contains both the name and the value.
95    ///
96    /// If the field is a dynamic object field, it returns the ID of the Object,
97    /// which is the value of the field.
98    ///
99    /// # Examples
100    ///
101    /// ```rust,no_run
102    /// use std::str::FromStr;
103    ///
104    /// use iota_sdk::{IotaClientBuilder, types::base_types::IotaAddress};
105    /// use iota_sdk_types::ObjectId;
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, rpc_types::IotaObjectDataOptions, types::base_types::IotaAddress,
188    /// };
189    /// use iota_sdk_types::ObjectId;
190    ///
191    /// #[tokio::main]
192    /// async fn main() -> Result<(), anyhow::Error> {
193    ///     let iota = IotaClientBuilder::default().build_testnet().await?;
194    ///     let address = IotaAddress::from_str("0x0000....0000")?;
195    ///     let owned_objects = iota
196    ///         .read_api()
197    ///         .get_owned_objects(address, None, None, None)
198    ///         .await?;
199    ///     // this code example assumes that there are previous owned objects
200    ///     let object = owned_objects
201    ///         .data
202    ///         .get(0)
203    ///         .expect(&format!("No owned objects for this address {}", address));
204    ///     let object_data = object.data.as_ref().expect(&format!(
205    ///         "No object data for this IotaObjectResponse {:?}",
206    ///         object
207    ///     ));
208    ///     let object_id = object_data.object_id;
209    ///     let version = object_data.version;
210    ///     let past_object = iota
211    ///         .read_api()
212    ///         .try_get_parsed_past_object(
213    ///             object_id,
214    ///             version,
215    ///             IotaObjectDataOptions {
216    ///                 show_type: true,
217    ///                 show_owner: true,
218    ///                 show_previous_transaction: true,
219    ///                 show_display: true,
220    ///                 show_content: true,
221    ///                 show_bcs: true,
222    ///                 show_storage_rebate: true,
223    ///             },
224    ///         )
225    ///         .await?;
226    ///     Ok(())
227    /// }
228    /// ```
229    pub async fn try_get_parsed_past_object(
230        &self,
231        object_id: ObjectId,
232        version: SequenceNumber,
233        options: IotaObjectDataOptions,
234    ) -> IotaRpcResult<IotaPastObjectResponse> {
235        Ok(self
236            .api
237            .http
238            .try_get_past_object(object_id, version, Some(options))
239            .await?)
240    }
241
242    /// Get a list of parsed past objects.
243    ///
244    /// See [Self::try_get_parsed_past_object] for more details about past
245    /// objects.
246    ///
247    /// # Examples
248    ///
249    /// ```rust,no_run
250    /// use std::str::FromStr;
251    ///
252    /// use iota_sdk::{
253    ///     IotaClientBuilder,
254    ///     rpc_types::{IotaGetPastObjectRequest, IotaObjectDataOptions},
255    ///     types::base_types::IotaAddress,
256    /// };
257    /// use iota_sdk_types::ObjectId;
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        let query_v2 = IotaTransactionBlockResponseQueryV2 {
516            filter: query.filter.as_ref().map(|f| f.as_v2()),
517            options: query.options,
518        };
519        self.query_transaction_blocks_v2(query_v2, cursor, limit, descending_order)
520            .await
521    }
522
523    /// Get filtered transaction blocks information.
524    /// Results are paginated.
525    pub async fn query_transaction_blocks_v2(
526        &self,
527        query: IotaTransactionBlockResponseQueryV2,
528        cursor: impl Into<Option<TransactionDigest>>,
529        limit: impl Into<Option<usize>>,
530        descending_order: bool,
531    ) -> IotaRpcResult<TransactionBlocksPage> {
532        Ok(self
533            .api
534            .http
535            .query_transaction_blocks_v2(query, cursor.into(), limit.into(), Some(descending_order))
536            .await?)
537    }
538
539    /// Get the first four bytes of the chain's genesis checkpoint digest in hex
540    /// format.
541    pub async fn get_chain_identifier(&self) -> IotaRpcResult<String> {
542        Ok(self.api.http.get_chain_identifier().await?)
543    }
544
545    /// Get a checkpoint by its ID.
546    pub async fn get_checkpoint(&self, id: CheckpointId) -> IotaRpcResult<Checkpoint> {
547        Ok(self.api.http.get_checkpoint(id).await?)
548    }
549
550    /// Return a list of checkpoints.
551    /// Results are paginated.
552    pub async fn get_checkpoints(
553        &self,
554        cursor: impl Into<Option<BigInt<u64>>>,
555        limit: impl Into<Option<usize>>,
556        descending_order: bool,
557    ) -> IotaRpcResult<CheckpointPage> {
558        Ok(self
559            .api
560            .http
561            .get_checkpoints(cursor.into(), limit.into(), descending_order)
562            .await?)
563    }
564
565    /// Get the sequence number of the latest checkpoint that has been executed.
566    pub async fn get_latest_checkpoint_sequence_number(
567        &self,
568    ) -> IotaRpcResult<CheckpointSequenceNumber> {
569        Ok(*self
570            .api
571            .http
572            .get_latest_checkpoint_sequence_number()
573            .await?)
574    }
575
576    /// Get a stream of transactions.
577    pub fn get_transactions_stream(
578        &self,
579        query: IotaTransactionBlockResponseQuery,
580        cursor: impl Into<Option<TransactionDigest>>,
581        descending_order: bool,
582    ) -> impl Stream<Item = IotaTransactionBlockResponse> + '_ {
583        let query_v2 = IotaTransactionBlockResponseQueryV2 {
584            filter: query.filter.as_ref().map(|f| f.as_v2()),
585            options: query.options,
586        };
587
588        self.get_transactions_stream_v2(query_v2, cursor, descending_order)
589    }
590
591    /// Get a stream of transactions.
592    pub fn get_transactions_stream_v2(
593        &self,
594        query: IotaTransactionBlockResponseQueryV2,
595        cursor: impl Into<Option<TransactionDigest>>,
596        descending_order: bool,
597    ) -> impl Stream<Item = IotaTransactionBlockResponse> + '_ {
598        let cursor = cursor.into();
599
600        stream::unfold(
601            (vec![], cursor, true, query),
602            move |(mut data, cursor, first, query)| async move {
603                if let Some(item) = data.pop() {
604                    Some((item, (data, cursor, false, query)))
605                } else if (cursor.is_none() && first) || cursor.is_some() {
606                    let page = self
607                        .query_transaction_blocks_v2(
608                            query.clone(),
609                            cursor,
610                            Some(100),
611                            descending_order,
612                        )
613                        .await
614                        .ok()?;
615                    let mut data = page.data;
616                    data.reverse();
617                    data.pop()
618                        .map(|item| (item, (data, page.next_cursor, false, query)))
619                } else {
620                    None
621                }
622            },
623        )
624    }
625
626    /// Subscribe to a stream of transactions.
627    ///
628    /// This is only available through WebSockets.
629    pub async fn subscribe_transaction(
630        &self,
631        filter: TransactionFilter,
632    ) -> IotaRpcResult<impl Stream<Item = IotaRpcResult<IotaTransactionBlockEffects>>> {
633        let Some(c) = &self.api.ws else {
634            return Err(Error::Subscription(
635                "Subscription only supported by WebSocket client.".to_string(),
636            ));
637        };
638        let subscription: Subscription<IotaTransactionBlockEffects> =
639            c.subscribe_transaction(filter).await?;
640        Ok(subscription.map(|item| Ok(item?)))
641    }
642
643    /// Get move modules by package ID, keyed by name.
644    pub async fn get_normalized_move_modules_by_package(
645        &self,
646        package: ObjectId,
647    ) -> IotaRpcResult<BTreeMap<String, IotaMoveNormalizedModule>> {
648        Ok(self
649            .api
650            .http
651            .get_normalized_move_modules_by_package(package)
652            .await?)
653    }
654
655    // TODO(devx): we can probably cache this given an epoch
656    /// Get the reference gas price.
657    pub async fn get_reference_gas_price(&self) -> IotaRpcResult<u64> {
658        Ok(*self.api.http.get_reference_gas_price().await?)
659    }
660
661    /// Dry run a transaction block given the provided transaction data.
662    ///
663    /// This simulates running the transaction, including all standard checks,
664    /// without actually running it. This is useful for estimating the gas fees
665    /// of a transaction before executing it. You can also use it to identify
666    /// any side-effects of a transaction before you execute it on the network.
667    pub async fn dry_run_transaction_block(
668        &self,
669        tx: TransactionData,
670    ) -> IotaRpcResult<DryRunTransactionBlockResponse> {
671        Ok(self
672            .api
673            .http
674            .dry_run_transaction_block(Base64::from_bytes(&bcs::to_bytes(&tx)?))
675            .await?)
676    }
677
678    /// Use this function to inspect the current state of the network by running
679    /// a programmable transaction block without committing its effects on
680    /// chain.
681    ///
682    /// Unlike a dry run, this method will not validate whether the transaction
683    /// block would succeed or fail under normal circumstances, e.g.:
684    ///
685    /// - Transaction inputs are not checked for ownership (i.e. you can
686    ///   construct calls involving objects you do not own)
687    /// - Calls are not checked for visibility (you can call private functions
688    ///   on modules)
689    /// - Inputs of any type can be constructed and passed in, including coins
690    ///   and other objects that would usually need to be constructed with a
691    ///   move call
692    /// - Function returns do not need to be used, even if they do not have
693    ///   `drop`
694    ///
695    /// This method's output includes a breakdown of results returned by every
696    /// transaction in the block, as well as the transaction's effects.
697    ///
698    /// To run an accurate simulation of a transaction and understand whether
699    /// it will successfully validate and run, use
700    /// [Self::dry_run_transaction_block] instead.
701    pub async fn dev_inspect_transaction_block(
702        &self,
703        sender_address: IotaAddress,
704        tx: TransactionKind,
705        gas_price: impl Into<Option<BigInt<u64>>>,
706        epoch: impl Into<Option<BigInt<u64>>>,
707        additional_args: impl Into<Option<DevInspectArgs>>,
708    ) -> IotaRpcResult<DevInspectResults> {
709        Ok(self
710            .api
711            .http
712            .dev_inspect_transaction_block(
713                sender_address,
714                Base64::from_bytes(&bcs::to_bytes(&tx)?),
715                gas_price.into(),
716                epoch.into(),
717                additional_args.into(),
718            )
719            .await?)
720    }
721
722    /// Get the protocol config by version.
723    ///
724    /// The version defaults to the current version.
725    pub async fn get_protocol_config(
726        &self,
727        version: impl Into<Option<BigInt<u64>>>,
728    ) -> IotaRpcResult<ProtocolConfigResponse> {
729        Ok(self.api.http.get_protocol_config(version.into()).await?)
730    }
731
732    /// Get an object by ID before the given version.
733    pub async fn try_get_object_before_version(
734        &self,
735        object_id: ObjectId,
736        version: SequenceNumber,
737    ) -> IotaRpcResult<IotaPastObjectResponse> {
738        Ok(self
739            .api
740            .http
741            .try_get_object_before_version(object_id, version)
742            .await?)
743    }
744
745    #[cfg(feature = "iota-names")]
746    /// Return the resolved record for the given name.
747    pub async fn iota_names_lookup(&self, name: &str) -> IotaRpcResult<Option<IotaNameRecord>> {
748        Ok(self.api.http.iota_names_lookup(name).await?)
749    }
750
751    #[cfg(feature = "iota-names")]
752    /// Return the resolved name for the given address.
753    pub async fn iota_names_reverse_lookup(
754        &self,
755        address: IotaAddress,
756    ) -> IotaRpcResult<Option<String>> {
757        Ok(self.api.http.iota_names_reverse_lookup(address).await?)
758    }
759
760    #[cfg(feature = "iota-names")]
761    /// Find all registration NFTs for the given address.
762    pub async fn iota_names_find_all_registration_nfts(
763        &self,
764        address: IotaAddress,
765        cursor: Option<ObjectId>,
766        limit: Option<usize>,
767        options: Option<IotaObjectDataOptions>,
768    ) -> IotaRpcResult<ObjectsPage> {
769        Ok(self
770            .api
771            .http
772            .iota_names_find_all_registration_nfts(address, cursor, limit, options)
773            .await?)
774    }
775}