1use serde::{Deserialize, Serialize};
4
5use super::CryptoHash;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub enum SyncCheckpoint {
11 Genesis,
13 EarliestAvailable,
15}
16
17#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum BlockReference {
22 Finality(Finality),
24 Height(u64),
26 Hash(CryptoHash),
28 SyncCheckpoint(SyncCheckpoint),
30}
31
32impl Default for BlockReference {
33 fn default() -> Self {
34 Self::Finality(Finality::Final)
35 }
36}
37
38impl BlockReference {
39 pub fn final_() -> Self {
41 Self::Finality(Finality::Final)
42 }
43
44 pub fn optimistic() -> Self {
46 Self::Finality(Finality::Optimistic)
47 }
48
49 pub fn near_final() -> Self {
51 Self::Finality(Finality::NearFinal)
52 }
53
54 pub fn at_height(height: u64) -> Self {
56 Self::Height(height)
57 }
58
59 pub fn at_hash(hash: CryptoHash) -> Self {
61 Self::Hash(hash)
62 }
63
64 pub fn genesis() -> Self {
66 Self::SyncCheckpoint(SyncCheckpoint::Genesis)
67 }
68
69 pub fn earliest_available() -> Self {
71 Self::SyncCheckpoint(SyncCheckpoint::EarliestAvailable)
72 }
73
74 pub fn to_rpc_params(&self) -> serde_json::Value {
76 match self {
77 BlockReference::Finality(f) => {
78 serde_json::json!({ "finality": f.as_str() })
79 }
80 BlockReference::Height(h) => {
81 serde_json::json!({ "block_id": *h })
82 }
83 BlockReference::Hash(h) => {
84 serde_json::json!({ "block_id": h.to_string() })
85 }
86 BlockReference::SyncCheckpoint(cp) => {
87 let cp_str = match cp {
88 SyncCheckpoint::Genesis => "genesis",
89 SyncCheckpoint::EarliestAvailable => "earliest_available",
90 };
91 serde_json::json!({ "sync_checkpoint": cp_str })
92 }
93 }
94 }
95}
96
97impl From<Finality> for BlockReference {
98 fn from(f: Finality) -> Self {
99 Self::Finality(f)
100 }
101}
102
103impl From<u64> for BlockReference {
104 fn from(height: u64) -> Self {
105 Self::Height(height)
106 }
107}
108
109impl From<CryptoHash> for BlockReference {
110 fn from(hash: CryptoHash) -> Self {
111 Self::Hash(hash)
112 }
113}
114
115#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
117#[serde(rename_all = "lowercase")]
118pub enum Finality {
119 Optimistic,
121 #[serde(rename = "near-final")]
123 NearFinal,
124 #[default]
126 Final,
127}
128
129impl Finality {
130 pub fn as_str(&self) -> &'static str {
132 match self {
133 Finality::Optimistic => "optimistic",
134 Finality::NearFinal => "near-final",
135 Finality::Final => "final",
136 }
137 }
138}
139
140#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
142#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
143pub enum TxExecutionStatus {
144 None,
146 Included,
148 #[default]
150 ExecutedOptimistic,
151 IncludedFinal,
153 Executed,
155 Final,
157}
158
159impl TxExecutionStatus {
160 pub fn as_str(&self) -> &'static str {
162 match self {
163 Self::None => "NONE",
164 Self::Included => "INCLUDED",
165 Self::ExecutedOptimistic => "EXECUTED_OPTIMISTIC",
166 Self::IncludedFinal => "INCLUDED_FINAL",
167 Self::Executed => "EXECUTED",
168 Self::Final => "FINAL",
169 }
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_block_reference_rpc_params() {
179 let final_ref = BlockReference::final_();
180 let params = final_ref.to_rpc_params();
181 assert_eq!(params["finality"], "final");
182
183 let height_ref = BlockReference::Height(12345);
184 let params = height_ref.to_rpc_params();
185 assert_eq!(params["block_id"], 12345);
186 }
187
188 #[test]
189 fn test_finality_as_str() {
190 assert_eq!(Finality::Final.as_str(), "final");
191 assert_eq!(Finality::Optimistic.as_str(), "optimistic");
192 assert_eq!(Finality::NearFinal.as_str(), "near-final");
193 }
194
195 #[test]
196 fn test_tx_execution_status_as_str() {
197 assert_eq!(TxExecutionStatus::Final.as_str(), "FINAL");
198 assert_eq!(
199 TxExecutionStatus::ExecutedOptimistic.as_str(),
200 "EXECUTED_OPTIMISTIC"
201 );
202 }
203
204 #[test]
205 fn test_block_reference_hash_to_rpc_params() {
206 let hash = CryptoHash::hash(b"test block");
207 let block_ref = BlockReference::at_hash(hash);
208 let params = block_ref.to_rpc_params();
209 assert_eq!(params["block_id"], hash.to_string());
210 }
211
212 #[test]
213 fn test_block_reference_constructors() {
214 let final_ref = BlockReference::final_();
216 assert!(matches!(
217 final_ref,
218 BlockReference::Finality(Finality::Final)
219 ));
220
221 let optimistic_ref = BlockReference::optimistic();
222 assert!(matches!(
223 optimistic_ref,
224 BlockReference::Finality(Finality::Optimistic)
225 ));
226
227 let near_final_ref = BlockReference::near_final();
228 assert!(matches!(
229 near_final_ref,
230 BlockReference::Finality(Finality::NearFinal)
231 ));
232
233 let height_ref = BlockReference::at_height(12345);
234 assert!(matches!(height_ref, BlockReference::Height(12345)));
235
236 let hash = CryptoHash::hash(b"test");
237 let hash_ref = BlockReference::at_hash(hash);
238 assert!(matches!(hash_ref, BlockReference::Hash(_)));
239 }
240
241 #[test]
242 fn test_block_reference_default() {
243 let default = BlockReference::default();
244 assert_eq!(default, BlockReference::Finality(Finality::Final));
245 }
246
247 #[test]
248 fn test_block_reference_from_finality() {
249 let block_ref: BlockReference = Finality::Optimistic.into();
250 assert_eq!(block_ref, BlockReference::Finality(Finality::Optimistic));
251 }
252
253 #[test]
254 fn test_block_reference_from_height() {
255 let block_ref: BlockReference = 99999u64.into();
256 assert_eq!(block_ref, BlockReference::Height(99999));
257 }
258
259 #[test]
260 fn test_block_reference_from_hash() {
261 let hash = CryptoHash::hash(b"block");
262 let block_ref: BlockReference = hash.into();
263 assert_eq!(block_ref, BlockReference::Hash(hash));
264 }
265
266 #[test]
267 fn test_finality_default() {
268 let default = Finality::default();
269 assert_eq!(default, Finality::Final);
270 }
271
272 #[test]
273 fn test_tx_execution_status_default() {
274 let default = TxExecutionStatus::default();
275 assert_eq!(default, TxExecutionStatus::ExecutedOptimistic);
276 }
277
278 #[test]
279 fn test_tx_execution_status_all_variants() {
280 assert_eq!(TxExecutionStatus::None.as_str(), "NONE");
281 assert_eq!(TxExecutionStatus::Included.as_str(), "INCLUDED");
282 assert_eq!(
283 TxExecutionStatus::ExecutedOptimistic.as_str(),
284 "EXECUTED_OPTIMISTIC"
285 );
286 assert_eq!(TxExecutionStatus::IncludedFinal.as_str(), "INCLUDED_FINAL");
287 assert_eq!(TxExecutionStatus::Executed.as_str(), "EXECUTED");
288 assert_eq!(TxExecutionStatus::Final.as_str(), "FINAL");
289 }
290
291 #[test]
292 fn test_finality_serde_roundtrip() {
293 for finality in [Finality::Optimistic, Finality::NearFinal, Finality::Final] {
295 let json = serde_json::to_string(&finality).unwrap();
296 let parsed: Finality = serde_json::from_str(&json).unwrap();
297 assert_eq!(finality, parsed);
298 }
299 }
300
301 #[test]
302 fn test_tx_execution_status_serde_roundtrip() {
303 for status in [
304 TxExecutionStatus::None,
305 TxExecutionStatus::Included,
306 TxExecutionStatus::ExecutedOptimistic,
307 TxExecutionStatus::IncludedFinal,
308 TxExecutionStatus::Executed,
309 TxExecutionStatus::Final,
310 ] {
311 let json = serde_json::to_string(&status).unwrap();
312 let parsed: TxExecutionStatus = serde_json::from_str(&json).unwrap();
313 assert_eq!(status, parsed);
314 }
315 }
316
317 #[test]
318 fn test_block_reference_clone_and_eq() {
319 let original = BlockReference::Height(12345);
320 let cloned = original.clone();
321 assert_eq!(original, cloned);
322 }
323
324 #[test]
325 fn test_finality_clone_and_copy() {
326 let f1 = Finality::Optimistic;
327 let f2 = f1; #[allow(clippy::clone_on_copy)]
329 let f3 = f1.clone(); assert_eq!(f1, f2);
331 assert_eq!(f1, f3);
332 }
333
334 #[test]
335 fn test_sync_checkpoint_constructors() {
336 let genesis = BlockReference::genesis();
337 assert!(matches!(
338 genesis,
339 BlockReference::SyncCheckpoint(SyncCheckpoint::Genesis)
340 ));
341
342 let earliest = BlockReference::earliest_available();
343 assert!(matches!(
344 earliest,
345 BlockReference::SyncCheckpoint(SyncCheckpoint::EarliestAvailable)
346 ));
347 }
348
349 #[test]
350 fn test_sync_checkpoint_rpc_params() {
351 let genesis = BlockReference::genesis();
352 let params = genesis.to_rpc_params();
353 assert_eq!(params["sync_checkpoint"], "genesis");
354
355 let earliest = BlockReference::earliest_available();
356 let params = earliest.to_rpc_params();
357 assert_eq!(params["sync_checkpoint"], "earliest_available");
358 }
359
360 #[test]
361 fn test_sync_checkpoint_serde_roundtrip() {
362 for cp in [SyncCheckpoint::Genesis, SyncCheckpoint::EarliestAvailable] {
363 let json = serde_json::to_string(&cp).unwrap();
364 let parsed: SyncCheckpoint = serde_json::from_str(&json).unwrap();
365 assert_eq!(cp, parsed);
366 }
367 }
368}