sui_indexer_alt_jsonrpc/api/objects/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use filter::SuiObjectResponseQuery;
5use futures::future;
6use jsonrpsee::core::RpcResult;
7use jsonrpsee::proc_macros::rpc;
8use mysten_common::ZipDebugEqIteratorExt;
9use sui_json_rpc_types::Page;
10use sui_json_rpc_types::SuiGetPastObjectRequest;
11use sui_json_rpc_types::SuiObjectDataOptions;
12use sui_json_rpc_types::SuiObjectResponse;
13use sui_json_rpc_types::SuiPastObjectResponse;
14use sui_open_rpc::Module;
15use sui_open_rpc_macros::open_rpc;
16use sui_types::base_types::ObjectID;
17use sui_types::base_types::SequenceNumber;
18use sui_types::base_types::SuiAddress;
19
20use crate::api::objects::error::Error;
21use crate::api::rpc_module::RpcModule;
22use crate::context::Context;
23use crate::error::InternalContext;
24use crate::error::invalid_params;
25
26mod error;
27pub(crate) mod filter;
28pub(crate) mod response;
29
30#[open_rpc(namespace = "sui", tag = "Objects API")]
31#[rpc(server, namespace = "sui")]
32trait ObjectsApi {
33    /// Return the object information for the latest version of an object.
34    #[method(name = "getObject")]
35    async fn get_object(
36        &self,
37        /// The ID of the queried obect
38        object_id: ObjectID,
39        /// Options for specifying the content to be returned
40        options: Option<SuiObjectDataOptions>,
41    ) -> RpcResult<SuiObjectResponse>;
42
43    /// Return the object information for the latest versions of multiple objects.
44    #[method(name = "multiGetObjects")]
45    async fn multi_get_objects(
46        &self,
47        /// the IDs of the queried objects
48        object_ids: Vec<ObjectID>,
49        /// Options for specifying the content to be returned
50        options: Option<SuiObjectDataOptions>,
51    ) -> RpcResult<Vec<SuiObjectResponse>>;
52
53    /// Return the object information for a specified version.
54    ///
55    /// Note that past versions of an object may be pruned from the system, even if they once
56    /// existed. Different RPC services may return different responses for the same request as a
57    /// result, based on their pruning policies.
58    #[method(name = "tryGetPastObject")]
59    async fn try_get_past_object(
60        &self,
61        /// The ID of the queried object
62        object_id: ObjectID,
63        /// The version of the queried object.
64        version: SequenceNumber,
65        /// Options for specifying the content to be returned
66        options: Option<SuiObjectDataOptions>,
67    ) -> RpcResult<SuiPastObjectResponse>;
68
69    /// Return the object information for multiple specified objects and versions.
70    ///
71    /// Note that past versions of an object may be pruned from the system, even if they once
72    /// existed. Different RPC services may return different responses for the same request as a
73    /// result, based on their pruning policies.
74    #[method(name = "tryMultiGetPastObjects")]
75    async fn try_multi_get_past_objects(
76        &self,
77        /// A vector of object and versions to be queried
78        past_objects: Vec<SuiGetPastObjectRequest>,
79        /// Options for specifying the content to be returned
80        options: Option<SuiObjectDataOptions>,
81    ) -> RpcResult<Vec<SuiPastObjectResponse>>;
82}
83
84#[open_rpc(namespace = "suix", tag = "Query Objects API")]
85#[rpc(server, namespace = "suix")]
86trait QueryObjectsApi {
87    /// Query objects by their owner's address. Returns a paginated list of objects.
88    ///
89    /// If a cursor is provided, the query will start from the object after the one pointed to by
90    /// this cursor, otherwise pagination starts from the first page of objects owned by the
91    /// address.
92    ///
93    /// The definition of "first" page is somewhat arbitrary. It is a page such that continuing to
94    /// paginate an address's objects from this page will eventually reach all objects owned by
95    /// that address assuming that the owned object set does not change. If the owned object set
96    /// does change, pagination may not be consistent (may not reflect a set of objects that the
97    /// address owned at a single point in time).
98    ///
99    /// The size of each page is controlled by the `limit` parameter.
100    #[method(name = "getOwnedObjects")]
101    async fn get_owned_objects(
102        &self,
103        /// The owner's address.
104        address: SuiAddress,
105        /// Additional querying criteria for the object.
106        query: Option<SuiObjectResponseQuery>,
107        /// Cursor to start paginating from.
108        cursor: Option<String>,
109        /// Maximum number of objects to return per page.
110        limit: Option<usize>,
111    ) -> RpcResult<Page<SuiObjectResponse, String>>;
112}
113
114pub(crate) struct Objects(pub Context);
115
116pub(crate) struct QueryObjects(pub Context);
117
118#[async_trait::async_trait]
119impl ObjectsApiServer for Objects {
120    async fn get_object(
121        &self,
122        object_id: ObjectID,
123        options: Option<SuiObjectDataOptions>,
124    ) -> RpcResult<SuiObjectResponse> {
125        let Self(ctx) = self;
126        let options = options.unwrap_or_default();
127        Ok(response::live_object(ctx, object_id, &options)
128            .await
129            .with_internal_context(|| {
130                format!("Failed to get object {object_id} at latest version")
131            })?)
132    }
133
134    async fn multi_get_objects(
135        &self,
136        object_ids: Vec<ObjectID>,
137        options: Option<SuiObjectDataOptions>,
138    ) -> RpcResult<Vec<SuiObjectResponse>> {
139        let Self(ctx) = self;
140        let config = &ctx.config().objects;
141        if object_ids.len() > config.max_multi_get_objects {
142            return Err(invalid_params(Error::TooManyKeys {
143                requested: object_ids.len(),
144                max: config.max_multi_get_objects,
145            })
146            .into());
147        }
148
149        let options = options.unwrap_or_default();
150
151        let obj_futures = object_ids
152            .iter()
153            .map(|id| response::live_object(ctx, *id, &options));
154
155        Ok(future::join_all(obj_futures)
156            .await
157            .into_iter()
158            .zip_debug_eq(object_ids)
159            .map(|(r, o)| {
160                r.with_internal_context(|| format!("Failed to get object {o} at latest version"))
161            })
162            .collect::<Result<Vec<_>, _>>()?)
163    }
164
165    async fn try_get_past_object(
166        &self,
167        object_id: ObjectID,
168        version: SequenceNumber,
169        options: Option<SuiObjectDataOptions>,
170    ) -> RpcResult<SuiPastObjectResponse> {
171        let Self(ctx) = self;
172        let options = options.unwrap_or_default();
173        Ok(response::past_object(ctx, object_id, version, &options)
174            .await
175            .with_internal_context(|| {
176                format!(
177                    "Failed to get object {object_id} at version {}",
178                    version.value()
179                )
180            })?)
181    }
182
183    async fn try_multi_get_past_objects(
184        &self,
185        past_objects: Vec<SuiGetPastObjectRequest>,
186        options: Option<SuiObjectDataOptions>,
187    ) -> RpcResult<Vec<SuiPastObjectResponse>> {
188        let Self(ctx) = self;
189        let config = &ctx.config().objects;
190        if past_objects.len() > config.max_multi_get_objects {
191            return Err(invalid_params(Error::TooManyKeys {
192                requested: past_objects.len(),
193                max: config.max_multi_get_objects,
194            })
195            .into());
196        }
197
198        let options = options.unwrap_or_default();
199
200        let obj_futures = past_objects
201            .iter()
202            .map(|obj| response::past_object(ctx, obj.object_id, obj.version, &options));
203
204        Ok(future::join_all(obj_futures)
205            .await
206            .into_iter()
207            .zip_debug_eq(past_objects)
208            .map(|(r, o)| {
209                let id = o.object_id;
210                let v = o.version;
211                r.with_internal_context(|| format!("Failed to get object {id} at version {v}"))
212            })
213            .collect::<Result<Vec<_>, _>>()?)
214    }
215}
216
217#[async_trait::async_trait]
218impl QueryObjectsApiServer for QueryObjects {
219    async fn get_owned_objects(
220        &self,
221        address: SuiAddress,
222        query: Option<SuiObjectResponseQuery>,
223        cursor: Option<String>,
224        limit: Option<usize>,
225    ) -> RpcResult<Page<SuiObjectResponse, String>> {
226        let Self(ctx) = self;
227
228        let query = query.unwrap_or_default();
229
230        let Page {
231            data: object_ids,
232            next_cursor,
233            has_next_page,
234        } = filter::owned_objects(ctx, address, &query.filter, cursor, limit).await?;
235
236        let options = query.options.unwrap_or_default();
237
238        let obj_futures = object_ids
239            .iter()
240            .map(|id| response::live_object(ctx, *id, &options));
241
242        let data = future::join_all(obj_futures)
243            .await
244            .into_iter()
245            .zip_debug_eq(object_ids)
246            .map(|(r, id)| {
247                r.with_internal_context(|| format!("Failed to get object {id} at latest version"))
248            })
249            .collect::<Result<Vec<_>, _>>()?;
250
251        Ok(Page {
252            data,
253            next_cursor,
254            has_next_page,
255        })
256    }
257}
258
259impl RpcModule for Objects {
260    fn schema(&self) -> Module {
261        ObjectsApiOpenRpc::module_doc()
262    }
263
264    fn into_impl(self) -> jsonrpsee::RpcModule<Self> {
265        self.into_rpc()
266    }
267}
268
269impl RpcModule for QueryObjects {
270    fn schema(&self) -> Module {
271        QueryObjectsApiOpenRpc::module_doc()
272    }
273
274    fn into_impl(self) -> jsonrpsee::RpcModule<Self> {
275        self.into_rpc()
276    }
277}