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