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_system_state_summary(
42 &self,
43 ) -> Result<sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary> {
44 use sui_types::sui_system_state::SuiSystemStateTrait;
45
46 let system_state = self.get_system_state()?;
47 let summary = system_state.into_sui_system_state_summary();
48
49 Ok(summary)
50 }
51
52 pub fn get_authenticator_state(
53 &self,
54 ) -> Result<Option<sui_types::authenticator_state::AuthenticatorStateInner>> {
55 sui_types::authenticator_state::get_authenticator_state(self.inner())
56 .map_err(StorageError::custom)
57 }
58
59 #[tracing::instrument(skip(self))]
60 pub fn get_transaction(
61 &self,
62 digest: sui_sdk_types::Digest,
63 ) -> crate::Result<(
64 sui_types::transaction::TransactionData,
65 Vec<sui_types::signature::GenericSignature>,
66 sui_types::effects::TransactionEffects,
67 Option<sui_types::effects::TransactionEvents>,
68 )> {
69 use sui_types::effects::TransactionEffectsAPI;
70
71 let transaction_digest = digest.into();
72
73 let transaction = (*self
74 .inner()
75 .get_transaction(&transaction_digest)
76 .ok_or(TransactionNotFoundError(digest))?)
77 .clone()
78 .into_inner();
79 let effects = self
80 .inner()
81 .get_transaction_effects(&transaction_digest)
82 .ok_or(TransactionNotFoundError(digest))?;
83 let events = if effects.events_digest().is_some() {
84 self.inner()
85 .get_events(effects.transaction_digest())
86 .ok_or(TransactionNotFoundError(digest))?
87 .pipe(Some)
88 } else {
89 None
90 };
91
92 let transaction = transaction.into_data().into_inner();
93 let signatures = transaction.tx_signatures;
94 let transaction = transaction.intent_message.value;
95
96 Ok((transaction, signatures, effects, events))
97 }
98
99 #[tracing::instrument(skip(self))]
100 pub fn get_transaction_read(
101 &self,
102 digest: sui_sdk_types::Digest,
103 ) -> crate::Result<TransactionRead> {
104 let (transaction, signatures, effects, events) = self.get_transaction(digest)?;
105
106 let checkpoint = self.inner().get_transaction_checkpoint(&(digest.into()));
107
108 let timestamp_ms = if let Some(checkpoint) = checkpoint {
109 self.inner()
110 .get_checkpoint_by_sequence_number(checkpoint)
111 .map(|checkpoint| checkpoint.timestamp_ms)
112 } else {
113 None
114 };
115
116 let unchanged_loaded_runtime_objects = self
117 .inner()
118 .get_unchanged_loaded_runtime_objects(&(digest.into()));
119
120 Ok(TransactionRead {
121 digest,
122 transaction,
123 signatures,
124 effects,
125 events,
126 checkpoint,
127 timestamp_ms,
128 unchanged_loaded_runtime_objects,
129 })
130 }
131
132 pub fn lookup_address_balance(
133 &self,
134 owner: sui_types::base_types::SuiAddress,
135 coin_type: move_core_types::language_storage::StructTag,
136 ) -> Option<u64> {
137 use sui_types::MoveTypeTagTraitGeneric;
138 use sui_types::SUI_ACCUMULATOR_ROOT_OBJECT_ID;
139 use sui_types::accumulator_root::AccumulatorKey;
140 use sui_types::dynamic_field::DynamicFieldKey;
141
142 let balance_type = sui_types::balance::Balance::type_tag(coin_type.into());
143
144 let key = AccumulatorKey { owner };
145 let key_type_tag = AccumulatorKey::get_type_tag(&[balance_type]);
146
147 DynamicFieldKey(SUI_ACCUMULATOR_ROOT_OBJECT_ID, key, key_type_tag)
148 .into_unbounded_id()
149 .unwrap()
150 .load_object(self.inner())
151 .and_then(|o| o.load_value::<u128>().ok())
152 .map(|balance| balance as u64)
153 }
154
155 pub fn get_lowest_available_checkpoint(&self) -> Result<u64, crate::RpcError> {
158 let lowest_available_checkpoint = self.inner().get_lowest_available_checkpoint()?;
160 let lowest_available_checkpoint_objects =
162 self.inner().get_lowest_available_checkpoint_objects()?;
163
164 Ok(lowest_available_checkpoint.max(lowest_available_checkpoint_objects))
166 }
167}
168
169#[derive(Debug)]
170pub struct TransactionRead {
171 pub digest: sui_sdk_types::Digest,
172 pub transaction: sui_types::transaction::TransactionData,
173 pub signatures: Vec<sui_types::signature::GenericSignature>,
174 pub effects: sui_types::effects::TransactionEffects,
175 pub events: Option<sui_types::effects::TransactionEvents>,
176 #[allow(unused)]
177 pub checkpoint: Option<u64>,
178 pub timestamp_ms: Option<u64>,
179 pub unchanged_loaded_runtime_objects: Option<Vec<ObjectKey>>,
180}
181
182#[derive(Debug)]
183pub struct TransactionNotFoundError(pub sui_sdk_types::Digest);
184
185impl std::fmt::Display for TransactionNotFoundError {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 write!(f, "Transaction {} not found", self.0)
188 }
189}
190
191impl std::error::Error for TransactionNotFoundError {}
192
193impl From<TransactionNotFoundError> for crate::RpcError {
194 fn from(value: TransactionNotFoundError) -> Self {
195 Self::new(tonic::Code::NotFound, value.to_string())
196 }
197}