iota_grpc_client/api/state/dynamic_fields.rs
1// Copyright (c) 2026 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! High-level API for listing dynamic fields.
5//!
6//! # Available Read Mask Fields
7//!
8//! - `kind` - the kind of dynamic field (field or object)
9//! - `parent` - the parent object ID
10//! - `field_id` - the field object ID
11//! - `child_id` - the child object ID (for dynamic object fields)
12//! - `name` - BCS-encoded field name
13//! - `value` - BCS-encoded field value
14//! - `value_type` - the Move type of the value
15//! - `field_object` - the full field object (sub-fields match `GetObjects`)
16//! - `child_object` - the full child object (sub-fields match `GetObjects`)
17
18use iota_grpc_types::v1::{
19 dynamic_field::DynamicField,
20 state_service::{ListDynamicFieldsRequest, state_service_client::StateServiceClient},
21};
22use iota_sdk_types::ObjectId;
23
24use crate::{
25 Client, InterceptedChannel,
26 api::{
27 LIST_DYNAMIC_FIELDS_READ_MASK, define_list_query, field_mask_with_default, proto_object_id,
28 },
29};
30
31define_list_query! {
32 /// Builder for listing dynamic fields of a parent object.
33 ///
34 /// Created by [`Client::list_dynamic_fields`]. Await directly for a
35 /// single page, or call [`.collect(limit)`](Self::collect) to
36 /// auto-paginate.
37 pub struct ListDynamicFieldsQuery {
38 service_client: StateServiceClient<InterceptedChannel>,
39 request: ListDynamicFieldsRequest,
40 item: DynamicField,
41 rpc_method: list_dynamic_fields,
42 items_field: dynamic_fields,
43 }
44}
45
46impl Client {
47 /// List dynamic fields owned by a parent object.
48 ///
49 /// Returns a query builder. Await it directly for a single page
50 /// (with access to `next_page_token`), or call `.collect(limit)` to
51 /// auto-paginate through all results.
52 ///
53 /// # Parameters
54 ///
55 /// - `parent` - The object ID of the parent object.
56 /// - `page_size` - Optional maximum number of fields per page.
57 /// - `page_token` - Optional continuation token from a previous page.
58 /// - `read_mask` - Optional field mask. If `None`, uses
59 /// [`LIST_DYNAMIC_FIELDS_READ_MASK`].
60 ///
61 /// # Examples
62 ///
63 /// Single page:
64 /// ```no_run
65 /// # use iota_grpc_client::Client;
66 /// # use iota_sdk_types::ObjectId;
67 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
68 /// let client = Client::connect("http://localhost:9000").await?;
69 /// let parent: ObjectId = "0x2".parse()?;
70 ///
71 /// let page = client.list_dynamic_fields(parent, None, None, None).await?;
72 /// for field in &page.body().items {
73 /// println!("Dynamic field: {:?}", field);
74 /// }
75 /// # Ok(())
76 /// # }
77 /// ```
78 ///
79 /// Auto-paginate:
80 /// ```no_run
81 /// # use iota_grpc_client::Client;
82 /// # use iota_sdk_types::ObjectId;
83 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
84 /// let client = Client::connect("http://localhost:9000").await?;
85 /// let parent: ObjectId = "0x2".parse()?;
86 ///
87 /// let all = client
88 /// .list_dynamic_fields(parent, Some(50), None, None)
89 /// .collect(None)
90 /// .await?;
91 /// for field in all.body() {
92 /// println!("Dynamic field: {:?}", field);
93 /// }
94 /// # Ok(())
95 /// # }
96 /// ```
97 pub fn list_dynamic_fields(
98 &self,
99 parent: ObjectId,
100 page_size: Option<u32>,
101 page_token: Option<prost::bytes::Bytes>,
102 read_mask: Option<&str>,
103 ) -> ListDynamicFieldsQuery {
104 let base_request = ListDynamicFieldsRequest::default()
105 .with_parent(proto_object_id(parent))
106 .with_read_mask(field_mask_with_default(
107 read_mask,
108 LIST_DYNAMIC_FIELDS_READ_MASK,
109 ));
110
111 ListDynamicFieldsQuery::new(
112 self.state_service_client(),
113 base_request,
114 self.max_decoding_message_size(),
115 page_size,
116 page_token,
117 )
118 }
119}