metaflux_client/rest/
explorer.rs1use serde::{Deserialize, Serialize};
7use serde_json::json;
8
9use crate::error::ClientError;
10use crate::rest::RestClient;
11
12#[derive(Debug)]
14pub struct Explorer<'a> {
15 pub(crate) client: &'a RestClient,
16}
17
18#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "snake_case")]
21pub struct Block {
22 pub height: u64,
24 pub hash: String,
26 pub parent_hash: String,
28 pub ts_ms: u64,
30 pub proposer: String,
32 pub tx_hashes: Vec<String>,
34 pub app_hash: String,
39}
40
41#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(rename_all = "snake_case")]
44pub struct Transaction {
45 pub hash: String,
47 pub block_height: u64,
49 pub action_type: String,
51 pub signer: String,
53 pub nonce: u64,
55 pub status: String,
57 #[serde(default)]
59 pub error: Option<String>,
60}
61
62impl<'a> Explorer<'a> {
63 pub async fn block_by_height(&self, height: u64) -> Result<Block, ClientError> {
68 self.client
69 .post_json(
70 "/explorer",
71 &json!({ "type": "block_by_height", "height": height }),
72 )
73 .await
74 }
75
76 pub async fn tx_by_hash(&self, hash: &str) -> Result<Transaction, ClientError> {
81 self.client
82 .post_json("/explorer", &json!({ "type": "tx_by_hash", "hash": hash }))
83 .await
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn block_round_trips() {
93 let b = Block {
94 height: 100,
95 hash: "0xdeadbeef".repeat(8),
96 parent_hash: "0xcafebabe".repeat(8),
97 ts_ms: 1_700_000_000_000,
98 proposer: "0x".to_string() + &"ab".repeat(20),
99 tx_hashes: vec!["0x01".into(), "0x02".into()],
100 app_hash: "0x".to_string() + &"00".repeat(32),
101 };
102 let j = serde_json::to_string(&b).unwrap();
103 let dec: Block = serde_json::from_str(&j).unwrap();
104 assert_eq!(b, dec);
105 }
106
107 #[test]
108 fn transaction_uses_snake_case_fields() {
109 let t = Transaction {
110 hash: "0x01".into(),
111 block_height: 100,
112 action_type: "submit_order".into(),
113 signer: "0xab".into(),
114 nonce: 1_700_000_000_000,
115 status: "accepted".into(),
116 error: None,
117 };
118 let j = serde_json::to_value(&t).unwrap();
119 for key in ["block_height", "action_type"] {
120 assert!(j.get(key).is_some(), "missing {key}");
121 }
122 for key in ["blockHeight", "actionType"] {
123 assert!(j.get(key).is_none(), "wire leak: {key}");
124 }
125 }
126}