1use std::sync::Arc;
5
6use mysten_common::ZipDebugEqIteratorExt;
7use sui_sdk_types::{EpochId, ValidatorCommittee};
8use sui_types::base_types::TransactionDigest;
9use sui_types::effects::TransactionEffectsAPI;
10use sui_types::storage::ObjectKey;
11use sui_types::storage::RpcStateReader;
12use sui_types::storage::error::{Error as StorageError, Result};
13use tap::Pipe;
14
15#[derive(Clone)]
16pub struct StateReader {
17 inner: Arc<dyn RpcStateReader>,
18}
19
20impl StateReader {
21 pub fn new(inner: Arc<dyn RpcStateReader>) -> Self {
22 Self { inner }
23 }
24
25 pub fn inner(&self) -> &Arc<dyn RpcStateReader> {
26 &self.inner
27 }
28
29 #[tracing::instrument(skip(self))]
30 pub fn get_committee(&self, epoch: EpochId) -> Option<ValidatorCommittee> {
31 self.inner
32 .get_committee(epoch)
33 .map(|committee| (*committee).clone().into())
34 }
35
36 #[tracing::instrument(skip(self))]
37 pub fn get_system_state(&self) -> Result<sui_types::sui_system_state::SuiSystemState> {
38 sui_types::sui_system_state::get_sui_system_state(self.inner())
39 .map_err(StorageError::custom)
40 .map_err(StorageError::custom)
41 }
42
43 #[tracing::instrument(skip(self))]
44 pub fn get_display_object_v2_by_type(
45 &self,
46 object_type: &move_core_types::language_storage::StructTag,
47 ) -> Option<sui_types::display_registry::Display> {
48 let object_id =
49 sui_types::display_registry::display_object_id(object_type.clone().into()).ok()?;
50
51 let object = self.inner.get_object(&object_id)?;
52
53 let move_object = object.data.try_as_move()?;
54
55 bcs::from_bytes(move_object.contents()).ok()
56 }
57
58 #[tracing::instrument(skip(self))]
59 pub fn get_system_state_summary(
60 &self,
61 ) -> Result<sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary> {
62 use sui_types::sui_system_state::SuiSystemStateTrait;
63
64 let system_state = self.get_system_state()?;
65 let summary = system_state.into_sui_system_state_summary();
66
67 Ok(summary)
68 }
69
70 pub fn get_authenticator_state(
71 &self,
72 ) -> Result<Option<sui_types::authenticator_state::AuthenticatorStateInner>> {
73 sui_types::authenticator_state::get_authenticator_state(self.inner())
74 .map_err(StorageError::custom)
75 }
76
77 #[tracing::instrument(skip(self))]
78 pub fn get_transaction(
79 &self,
80 digest: sui_sdk_types::Digest,
81 ) -> crate::Result<(
82 sui_types::transaction::TransactionData,
83 Vec<sui_types::signature::GenericSignature>,
84 sui_types::effects::TransactionEffects,
85 Option<sui_types::effects::TransactionEvents>,
86 )> {
87 let transaction_digest = digest.into();
88
89 let transaction = (*self
90 .inner()
91 .get_transaction(&transaction_digest)
92 .ok_or(TransactionNotFoundError(digest))?)
93 .clone()
94 .into_inner();
95 let effects = self
96 .inner()
97 .get_transaction_effects(&transaction_digest)
98 .ok_or(TransactionNotFoundError(digest))?;
99 let events = if effects.events_digest().is_some() {
100 self.inner()
101 .get_events(effects.transaction_digest())
102 .ok_or(TransactionNotFoundError(digest))?
103 .pipe(Some)
104 } else {
105 None
106 };
107
108 let transaction = transaction.into_data().into_inner();
109 let signatures = transaction.tx_signatures;
110 let transaction = transaction.intent_message.value;
111
112 Ok((transaction, signatures, effects, events))
113 }
114
115 pub fn multi_get_transaction_reads(
116 &self,
117 digests: &[sui_sdk_types::Digest],
118 ) -> crate::Result<Vec<TransactionRead>> {
119 let transaction_digests = digests
120 .iter()
121 .copied()
122 .map(Into::into)
123 .collect::<Vec<TransactionDigest>>();
124 let transactions = self.inner().multi_get_transactions(&transaction_digests);
125 let effects = self
126 .inner()
127 .multi_get_transaction_effects(&transaction_digests);
128 let events = self.inner().multi_get_events(&transaction_digests);
129
130 let mut reads = Vec::with_capacity(digests.len());
131 for (((digest, transaction_digest), transaction), (effects, events)) in digests
132 .iter()
133 .copied()
134 .zip_debug_eq(transaction_digests)
135 .zip_debug_eq(transactions)
136 .zip_debug_eq(effects.into_iter().zip_debug_eq(events))
137 {
138 let transaction = (*transaction.ok_or(TransactionNotFoundError(digest))?)
139 .clone()
140 .into_inner();
141 let effects = effects.ok_or(TransactionNotFoundError(digest))?;
142 let events = if effects.events_digest().is_some() {
143 events.ok_or(TransactionNotFoundError(digest))?.pipe(Some)
144 } else {
145 None
146 };
147
148 let transaction = transaction.into_data().into_inner();
149 let signatures = transaction.tx_signatures;
150 let transaction = transaction.intent_message.value;
151
152 let checkpoint = self.inner().get_transaction_checkpoint(&transaction_digest);
153 let timestamp_ms = if let Some(checkpoint) = checkpoint {
154 self.inner()
155 .get_checkpoint_by_sequence_number(checkpoint)
156 .map(|checkpoint| checkpoint.timestamp_ms)
157 } else {
158 None
159 };
160
161 let unchanged_loaded_runtime_objects = self
162 .inner()
163 .get_unchanged_loaded_runtime_objects(&transaction_digest);
164
165 reads.push(TransactionRead {
166 digest,
167 transaction,
168 signatures,
169 effects,
170 events,
171 checkpoint,
172 timestamp_ms,
173 unchanged_loaded_runtime_objects,
174 });
175 }
176
177 Ok(reads)
178 }
179
180 pub fn multi_get_events(
181 &self,
182 digests: &[TransactionDigest],
183 ) -> Vec<Option<sui_types::effects::TransactionEvents>> {
184 self.inner().multi_get_events(digests)
185 }
186
187 #[tracing::instrument(skip(self))]
188 pub fn get_transaction_read(
189 &self,
190 digest: sui_sdk_types::Digest,
191 ) -> crate::Result<TransactionRead> {
192 let (transaction, signatures, effects, events) = self.get_transaction(digest)?;
193
194 let checkpoint = self.inner().get_transaction_checkpoint(&(digest.into()));
195
196 let timestamp_ms = if let Some(checkpoint) = checkpoint {
197 self.inner()
198 .get_checkpoint_by_sequence_number(checkpoint)
199 .map(|checkpoint| checkpoint.timestamp_ms)
200 } else {
201 None
202 };
203
204 let unchanged_loaded_runtime_objects = self
205 .inner()
206 .get_unchanged_loaded_runtime_objects(&(digest.into()));
207
208 Ok(TransactionRead {
209 digest,
210 transaction,
211 signatures,
212 effects,
213 events,
214 checkpoint,
215 timestamp_ms,
216 unchanged_loaded_runtime_objects,
217 })
218 }
219
220 pub fn lookup_address_balance(
221 &self,
222 owner: sui_types::base_types::SuiAddress,
223 coin_type: move_core_types::language_storage::StructTag,
224 ) -> Option<u64> {
225 use sui_types::MoveTypeTagTraitGeneric;
226 use sui_types::SUI_ACCUMULATOR_ROOT_OBJECT_ID;
227 use sui_types::accumulator_root::AccumulatorKey;
228 use sui_types::dynamic_field::DynamicFieldKey;
229
230 let balance_type = sui_types::balance::Balance::type_tag(coin_type.into());
231
232 let key = AccumulatorKey { owner };
233 let key_type_tag = AccumulatorKey::get_type_tag(&[balance_type]);
234
235 DynamicFieldKey(SUI_ACCUMULATOR_ROOT_OBJECT_ID, key, key_type_tag)
236 .into_unbounded_id()
237 .unwrap()
238 .load_object(self.inner())
239 .and_then(|o| o.load_value::<u128>().ok())
240 .map(|balance| balance as u64)
241 }
242
243 pub fn get_lowest_available_checkpoint(&self) -> Result<u64, crate::RpcError> {
246 let lowest_available_checkpoint = self.inner().get_lowest_available_checkpoint()?;
248 let lowest_available_checkpoint_objects =
250 self.inner().get_lowest_available_checkpoint_objects()?;
251
252 Ok(lowest_available_checkpoint.max(lowest_available_checkpoint_objects))
254 }
255}
256
257#[derive(Debug)]
258pub struct TransactionRead {
259 pub digest: sui_sdk_types::Digest,
260 pub transaction: sui_types::transaction::TransactionData,
261 pub signatures: Vec<sui_types::signature::GenericSignature>,
262 pub effects: sui_types::effects::TransactionEffects,
263 pub events: Option<sui_types::effects::TransactionEvents>,
264 #[allow(unused)]
265 pub checkpoint: Option<u64>,
266 pub timestamp_ms: Option<u64>,
267 pub unchanged_loaded_runtime_objects: Option<Vec<ObjectKey>>,
268}
269
270#[derive(Debug)]
271pub struct TransactionNotFoundError(pub sui_sdk_types::Digest);
272
273impl std::fmt::Display for TransactionNotFoundError {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 write!(f, "Transaction {} not found", self.0)
276 }
277}
278
279impl std::error::Error for TransactionNotFoundError {}
280
281impl From<TransactionNotFoundError> for crate::RpcError {
282 fn from(value: TransactionNotFoundError) -> Self {
283 Self::new(tonic::Code::NotFound, value.to_string())
284 }
285}
286
287pub struct DisplayStore<'s> {
288 state: &'s StateReader,
289}
290
291impl<'s> DisplayStore<'s> {
292 pub fn new(state: &'s StateReader) -> Self {
293 Self { state }
294 }
295}
296
297#[async_trait::async_trait]
298impl sui_display::v2::Store for DisplayStore<'_> {
299 async fn latest(
300 &self,
301 id: move_core_types::account_address::AccountAddress,
302 ) -> anyhow::Result<Option<(move_core_types::annotated_value::MoveTypeLayout, Vec<u8>)>> {
303 let Some(object) = self.state.inner().get_object(&id.into()) else {
304 return Ok(None);
305 };
306
307 let Some(move_object) = object.data.try_as_move() else {
308 return Ok(None);
309 };
310
311 let object_type = move_object.type_().clone().into();
312
313 let Some(layout) = self.state.inner().get_struct_layout(&object_type)? else {
314 return Ok(None);
315 };
316
317 Ok(Some((layout, move_object.contents().to_vec())))
318 }
319}