iota_grpc_client/api/ledger/
objects.rs

1// Copyright (c) 2026 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! High-level API for object queries.
5
6use iota_grpc_types::v1::{
7    ledger_service::{GetObjectsRequest, ObjectRequest, ObjectRequests},
8    object::Object,
9    types::ObjectReference,
10};
11use iota_sdk_types::{ObjectId, Version};
12
13use crate::{
14    Client,
15    api::{
16        Error, GET_OBJECTS_READ_MASK, MetadataEnvelope, ProtoResult, Result, collect_stream,
17        field_mask_with_default, proto_object_id, saturating_usize_to_u32,
18    },
19};
20
21impl Client {
22    /// Get objects by their IDs and optional versions.
23    ///
24    /// Returns proto `Object` types. Use `obj.object()` to convert to SDK
25    /// type, or use `obj.object_reference()` to get the object reference.
26    ///
27    /// Results are returned in the same order as the input refs.
28    /// If an object is not found, an error is returned.
29    ///
30    /// # Errors
31    ///
32    /// Returns [`Error::EmptyRequest`] if `refs` is empty.
33    ///
34    /// # Available Read Mask Fields
35    ///
36    /// The optional `read_mask` parameter controls which fields the server
37    /// returns. If `None`, uses [`GET_OBJECTS_READ_MASK`].
38    ///
39    /// ## Reference Fields
40    /// - `reference` - includes all reference fields
41    ///   - `reference.object_id` - the ID of the object to fetch
42    ///   - `reference.version` - the version of the object, which can be used
43    ///     to fetch a specific historical version or the latest version if not
44    ///     provided
45    ///   - `reference.digest` - the digest of the object contents, which can be
46    ///     used for integrity verification
47    ///
48    /// ## Data Fields
49    /// - `bcs` - the full BCS-encoded object
50    ///
51    /// # Example
52    ///
53    /// ```no_run
54    /// # use iota_grpc_client::Client;
55    /// # use iota_sdk_types::ObjectId;
56    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
57    /// let client = Client::connect("http://localhost:9000").await?;
58    /// let object_id: ObjectId = "0x2".parse()?;
59    ///
60    /// // Get proto objects
61    /// let objs = client.get_objects(&[(object_id, None)], None).await?;
62    ///
63    /// for obj in objs.body() {
64    ///     // Convert proto object to SDK type
65    ///     let sdk_obj = obj.object()?;
66    ///     println!("Got object ID: {:?}", sdk_obj.object_id());
67    ///     let obj_ref = obj.object_reference()?;
68    ///     println!("Object version: {:?}", obj_ref.version());
69    /// }
70    /// # Ok(())
71    /// # }
72    /// ```
73    pub async fn get_objects(
74        &self,
75        refs: &[(ObjectId, Option<Version>)],
76        read_mask: Option<&str>,
77    ) -> Result<MetadataEnvelope<Vec<Object>>> {
78        if refs.is_empty() {
79            return Err(Error::EmptyRequest);
80        }
81
82        let requests = ObjectRequests::default().with_requests(
83            refs.iter()
84                .map(|(id, version)| {
85                    let mut object_ref =
86                        ObjectReference::default().with_object_id(proto_object_id(*id));
87
88                    if let Some(v) = version {
89                        object_ref = object_ref.with_version(*v);
90                    }
91
92                    ObjectRequest::default().with_object_ref(object_ref)
93                })
94                .collect(),
95        );
96
97        let mut request = GetObjectsRequest::default()
98            .with_requests(requests)
99            .with_read_mask(field_mask_with_default(read_mask, GET_OBJECTS_READ_MASK));
100
101        if let Some(max_size) = self.max_decoding_message_size() {
102            request = request.with_max_message_size_bytes(saturating_usize_to_u32(max_size));
103        }
104
105        let mut client = self.ledger_service_client();
106
107        let response = client.get_objects(request).await?;
108        let (stream, metadata) = MetadataEnvelope::from(response).into_parts();
109
110        // Server guarantees results are returned in request order
111        collect_stream(stream, metadata, |msg| {
112            let items = msg
113                .objects
114                .into_iter()
115                .map(|r| r.into_result())
116                .collect::<Result<Vec<_>>>()?;
117            Ok((msg.has_next, items))
118        })
119        .await
120    }
121}