1use async_trait::async_trait;
5use jsonrpsee::RpcModule;
6use jsonrpsee::core::RpcResult;
7use sui_json_rpc::error::SuiRpcInputError;
8use sui_types::error::SuiObjectResponseError;
9use sui_types::object::ObjectRead;
10
11use crate::errors::IndexerError;
12use crate::indexer_reader::IndexerReader;
13use sui_json_rpc::SuiRpcModule;
14use sui_json_rpc_api::{QUERY_MAX_RESULT_LIMIT, ReadApiServer};
15use sui_json_rpc_types::ZkLoginIntentScope;
16use sui_json_rpc_types::ZkLoginVerifyResult;
17use sui_json_rpc_types::{
18 Checkpoint, CheckpointId, CheckpointPage, ProtocolConfigResponse, SuiEvent,
19 SuiGetPastObjectRequest, SuiObjectDataOptions, SuiObjectResponse, SuiPastObjectResponse,
20 SuiTransactionBlockResponse, SuiTransactionBlockResponseOptions,
21};
22use sui_open_rpc::Module;
23use sui_protocol_config::{ProtocolConfig, ProtocolVersion};
24use sui_types::base_types::SuiAddress;
25use sui_types::base_types::{ObjectID, SequenceNumber};
26use sui_types::digests::{ChainIdentifier, TransactionDigest};
27use sui_types::sui_serde::BigInt;
28
29#[derive(Clone)]
30pub struct ReadApi {
31 inner: IndexerReader,
32}
33
34impl ReadApi {
35 pub fn new(inner: IndexerReader) -> Self {
36 Self { inner }
37 }
38
39 async fn get_checkpoint(&self, id: CheckpointId) -> Result<Checkpoint, IndexerError> {
40 match self.inner.get_checkpoint(id).await {
41 Ok(Some(epoch_info)) => Ok(epoch_info),
42 Ok(None) => Err(IndexerError::InvalidArgumentError(format!(
43 "Checkpoint {id:?} not found"
44 ))),
45 Err(e) => Err(e),
46 }
47 }
48
49 async fn get_latest_checkpoint(&self) -> Result<Checkpoint, IndexerError> {
50 self.inner.get_latest_checkpoint().await
51 }
52
53 async fn get_chain_identifier(&self) -> RpcResult<ChainIdentifier> {
54 let genesis_checkpoint = self.get_checkpoint(CheckpointId::SequenceNumber(0)).await?;
55 Ok(ChainIdentifier::from(genesis_checkpoint.digest))
56 }
57}
58
59#[async_trait]
60impl ReadApiServer for ReadApi {
61 async fn get_object(
62 &self,
63 object_id: ObjectID,
64 options: Option<SuiObjectDataOptions>,
65 ) -> RpcResult<SuiObjectResponse> {
66 let object_read = self.inner.get_object_read(object_id).await?;
67 object_read_to_object_response(object_read, options.unwrap_or_default()).await
68 }
69
70 async fn multi_get_objects(
74 &self,
75 object_ids: Vec<ObjectID>,
76 options: Option<SuiObjectDataOptions>,
77 ) -> RpcResult<Vec<SuiObjectResponse>> {
78 if object_ids.len() > *QUERY_MAX_RESULT_LIMIT {
79 return Err(
80 SuiRpcInputError::SizeLimitExceeded(QUERY_MAX_RESULT_LIMIT.to_string()).into(),
81 );
82 }
83 let stored_objects = self.inner.multi_get_objects(object_ids).await?;
84 let options = options.unwrap_or_default();
85
86 let futures = stored_objects.into_iter().map(|stored_object| async {
87 let object_read = stored_object
88 .try_into_object_read(self.inner.package_resolver())
89 .await?;
90 object_read_to_object_response(object_read, options.clone()).await
91 });
92
93 let mut objects = futures::future::try_join_all(futures).await?;
94 objects.sort_by_key(|obj| obj.data.as_ref().map(|data| data.object_id));
96
97 Ok(objects)
98 }
99
100 async fn get_total_transaction_blocks(&self) -> RpcResult<BigInt<u64>> {
101 let checkpoint = self.get_latest_checkpoint().await?;
102 Ok(BigInt::from(checkpoint.network_total_transactions))
103 }
104
105 async fn get_transaction_block(
106 &self,
107 digest: TransactionDigest,
108 options: Option<SuiTransactionBlockResponseOptions>,
109 ) -> RpcResult<SuiTransactionBlockResponse> {
110 let mut txn = self
111 .multi_get_transaction_blocks(vec![digest], options)
112 .await?;
113
114 let txn = txn.pop().ok_or_else(|| {
115 IndexerError::InvalidArgumentError(format!("Transaction {digest} not found"))
116 })?;
117
118 Ok(txn)
119 }
120
121 async fn multi_get_transaction_blocks(
122 &self,
123 digests: Vec<TransactionDigest>,
124 options: Option<SuiTransactionBlockResponseOptions>,
125 ) -> RpcResult<Vec<SuiTransactionBlockResponse>> {
126 let num_digests = digests.len();
127 if num_digests > *QUERY_MAX_RESULT_LIMIT {
128 Err(SuiRpcInputError::SizeLimitExceeded(
129 QUERY_MAX_RESULT_LIMIT.to_string(),
130 ))?
131 }
132
133 let options = options.unwrap_or_default();
134 let txns = self
135 .inner
136 .multi_get_transaction_block_response_in_blocking_task(digests, options)
137 .await?;
138
139 Ok(txns)
140 }
141
142 async fn try_get_past_object(
143 &self,
144 _object_id: ObjectID,
145 _version: SequenceNumber,
146 _options: Option<SuiObjectDataOptions>,
147 ) -> RpcResult<SuiPastObjectResponse> {
148 Err(jsonrpsee::types::error::ErrorCode::MethodNotFound.into())
149 }
150
151 async fn try_get_object_before_version(
152 &self,
153 _: ObjectID,
154 _: SequenceNumber,
155 ) -> RpcResult<SuiPastObjectResponse> {
156 Err(jsonrpsee::types::error::ErrorCode::MethodNotFound.into())
157 }
158
159 async fn try_multi_get_past_objects(
160 &self,
161 _past_objects: Vec<SuiGetPastObjectRequest>,
162 _options: Option<SuiObjectDataOptions>,
163 ) -> RpcResult<Vec<SuiPastObjectResponse>> {
164 Err(jsonrpsee::types::error::ErrorCode::MethodNotFound.into())
165 }
166
167 async fn get_latest_checkpoint_sequence_number(&self) -> RpcResult<BigInt<u64>> {
168 let checkpoint = self.get_latest_checkpoint().await?;
169 Ok(BigInt::from(checkpoint.sequence_number))
170 }
171
172 async fn get_checkpoint(&self, id: CheckpointId) -> RpcResult<Checkpoint> {
173 self.get_checkpoint(id).await.map_err(Into::into)
174 }
175
176 async fn get_checkpoints(
177 &self,
178 cursor: Option<BigInt<u64>>,
179 limit: Option<usize>,
180 descending_order: bool,
181 ) -> RpcResult<CheckpointPage> {
182 let cursor = cursor.map(BigInt::into_inner);
183 let limit = sui_json_rpc_api::validate_limit(
184 limit,
185 sui_json_rpc_api::QUERY_MAX_RESULT_LIMIT_CHECKPOINTS,
186 )
187 .map_err(SuiRpcInputError::from)?;
188
189 let mut checkpoints = self
190 .inner
191 .get_checkpoints(cursor, limit + 1, descending_order)
192 .await?;
193
194 let has_next_page = checkpoints.len() > limit;
195 checkpoints.truncate(limit);
196
197 let next_cursor = checkpoints.last().map(|d| d.sequence_number.into());
198
199 Ok(CheckpointPage {
200 data: checkpoints,
201 next_cursor,
202 has_next_page,
203 })
204 }
205
206 async fn get_events(&self, transaction_digest: TransactionDigest) -> RpcResult<Vec<SuiEvent>> {
207 self.inner
208 .get_transaction_events(transaction_digest)
209 .await
210 .map_err(Into::into)
211 }
212
213 async fn get_protocol_config(
214 &self,
215 version: Option<BigInt<u64>>,
216 ) -> RpcResult<ProtocolConfigResponse> {
217 let chain = self.get_chain_identifier().await?.chain();
218 let version = if let Some(version) = version {
219 (*version).into()
220 } else {
221 let latest_epoch = self.inner.get_latest_epoch_info_from_db().await?;
222 (latest_epoch.protocol_version as u64).into()
223 };
224
225 ProtocolConfig::get_for_version_if_supported(version, chain)
226 .ok_or(SuiRpcInputError::ProtocolVersionUnsupported(
227 ProtocolVersion::MIN.as_u64(),
228 ProtocolVersion::MAX.as_u64(),
229 ))
230 .map_err(Into::into)
231 .map(ProtocolConfigResponse::from)
232 }
233
234 async fn get_chain_identifier(&self) -> RpcResult<String> {
235 self.get_chain_identifier().await.map(|id| id.to_string())
236 }
237
238 async fn verify_zklogin_signature(
239 &self,
240 _bytes: String,
241 _signature: String,
242 _intent_scope: ZkLoginIntentScope,
243 _author: SuiAddress,
244 ) -> RpcResult<ZkLoginVerifyResult> {
245 Err(jsonrpsee::types::error::ErrorCode::MethodNotFound.into())
246 }
247}
248
249impl SuiRpcModule for ReadApi {
250 fn rpc(self) -> RpcModule<Self> {
251 self.into_rpc()
252 }
253
254 fn rpc_doc_module() -> Module {
255 sui_json_rpc_api::ReadApiOpenRpc::module_doc()
256 }
257}
258
259async fn object_read_to_object_response(
260 object_read: ObjectRead,
261 options: SuiObjectDataOptions,
262) -> RpcResult<SuiObjectResponse> {
263 match object_read {
264 ObjectRead::NotExists(id) => Ok(SuiObjectResponse::new_with_error(
265 SuiObjectResponseError::NotExists { object_id: id },
266 )),
267 ObjectRead::Exists(object_ref, o, layout) => {
268 if options.show_display {
269 return Err(IndexerError::NotSupportedError(
270 "Display fields are not supported".to_owned(),
271 )
272 .into());
273 }
274 Ok(SuiObjectResponse::new_with_data(
275 (object_ref, o, layout, options, None)
276 .try_into()
277 .map_err(IndexerError::from)?,
278 ))
279 }
280 ObjectRead::Deleted((object_id, version, digest)) => Ok(SuiObjectResponse::new_with_error(
281 SuiObjectResponseError::Deleted {
282 object_id,
283 version,
284 digest,
285 },
286 )),
287 }
288}