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