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