sui_jsonrpc/msgs/
sui_event.rs1use std::fmt;
5use std::fmt::Display;
6
7use af_sui_types::{
8 Address as SuiAddress,
9 Identifier,
10 ObjectId,
11 StructTag,
12 TransactionDigest,
13 encode_base64_default,
14};
15use json_to_table::json_to_table;
16use serde::{Deserialize, Serialize};
17use serde_json::{Value, json};
18use serde_with::{DisplayFromStr, IfIsHumanReadable, serde_as};
19use tabled::settings::Style as TableStyle;
20
21use super::Page;
22use crate::serde::{Base64orBase58, BigInt};
23
24pub type EventPage = Page<SuiEvent, EventID>;
25
26#[serde_as]
27#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
28#[serde(rename = "Event", rename_all = "camelCase")]
29pub struct SuiEvent {
30 pub id: EventID,
36 pub package_id: ObjectId,
38 #[serde_as(as = "DisplayFromStr")]
40 pub transaction_module: Identifier,
41 pub sender: SuiAddress,
43 #[serde_as(as = "DisplayFromStr")]
46 pub type_: StructTag,
47 pub parsed_json: Value,
49 #[serde_as(as = "Base64orBase58")]
51 pub bcs: Vec<u8>,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 #[serde_as(as = "Option<BigInt<u64>>")]
55 pub timestamp_ms: Option<u64>,
56}
57
58impl Display for SuiEvent {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 let parsed_json = &mut self.parsed_json.clone();
61 bytes_array_to_base64(parsed_json);
62 let mut table = json_to_table(parsed_json);
63 let style = TableStyle::modern();
64 table.collapse().with(style);
65 write!(
66 f,
67 " ┌──\n │ EventID: {}:{}\n │ PackageID: {}\n │ Transaction Module: {}\n │ Sender: {}\n │ EventType: {}\n",
68 self.id.tx_digest,
69 self.id.event_seq,
70 self.package_id,
71 self.transaction_module,
72 self.sender,
73 self.type_
74 )?;
75 if let Some(ts) = self.timestamp_ms {
76 writeln!(f, " │ Timestamp: {}\n └──", ts)?;
77 }
78 writeln!(f, " │ ParsedJSON:")?;
79 let table_string = table.to_string();
80 let table_rows = table_string.split_inclusive('\n');
81 for r in table_rows {
82 write!(f, " │ {r}")?;
83 }
84
85 write!(f, "\n └──")
86 }
87}
88
89fn bytes_array_to_base64(v: &mut Value) {
91 match v {
92 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => (),
93 Value::Array(vals) => {
94 if let Some(vals) = vals.iter().map(try_into_byte).collect::<Option<Vec<_>>>() {
95 *v = json!(encode_base64_default(vals))
96 } else {
97 for val in vals {
98 bytes_array_to_base64(val)
99 }
100 }
101 }
102 Value::Object(map) => {
103 for val in map.values_mut() {
104 bytes_array_to_base64(val)
105 }
106 }
107 }
108}
109
110fn try_into_byte(v: &Value) -> Option<u8> {
112 let num = v.as_u64()?;
113 (num <= 255).then_some(num as u8)
114}
115
116#[serde_as]
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
120#[serde(rename_all = "camelCase")]
121pub struct EventID {
122 pub tx_digest: TransactionDigest,
123 #[serde_as(as = "IfIsHumanReadable<BigInt<u64>, _>")]
124 pub event_seq: u64,
125}
126
127#[serde_as]
128#[derive(Clone, Debug, Serialize, Deserialize)]
129pub enum EventFilter {
130 All([Box<EventFilter>; 0]),
132 Sender(SuiAddress),
134 Transaction(
136 TransactionDigest,
138 ),
139 MoveModule {
144 package: ObjectId,
146 #[serde_as(as = "DisplayFromStr")]
148 module: Identifier,
149 },
150 MoveEventType(#[serde_as(as = "DisplayFromStr")] StructTag),
154 MoveEventModule {
159 package: ObjectId,
161 #[serde_as(as = "DisplayFromStr")]
163 module: Identifier,
164 },
165 #[serde(rename_all = "camelCase")]
167 TimeRange {
168 #[serde_as(as = "BigInt<u64>")]
170 start_time: u64,
171 #[serde_as(as = "BigInt<u64>")]
173 end_time: u64,
174 },
175}
176
177#[cfg(test)]
178mod test {
179 use super::*;
180
181 const NEW_EVENT_JSON: &str = r#"{
182 "id": {
183 "txDigest": "BwwTktCxZryxsRdQ8JqFNYKYZDLDCQ3L59LCtQzgJgEo",
184 "eventSeq": "0"
185 },
186 "packageId": "0x0000000000000000000000000000000000000000000000000000000000000003",
187 "transactionModule": "sui_system",
188 "sender": "0x0000000000000000000000000000000000000000000000000000000000000000",
189 "type": "0x3::validator::StakingRequestEvent",
190 "parsedJson": {
191 "amount": "3680004485920",
192 "epoch": "0",
193 "pool_id": "0x568e13ac056b900ee3ba2f7c85f0c62e19cd25a14ea6f064c3799870ff7d0a9a",
194 "staker_address": "0x44b1b319e23495995fc837dafd28fc6af8b645edddff0fc1467f1ad631362c23",
195 "validator_address": "0x44b1b319e23495995fc837dafd28fc6af8b645edddff0fc1467f1ad631362c23"
196 },
197 "bcsEncoding": "base64",
198 "bcs": "Vo4TrAVrkA7jui98hfDGLhnNJaFOpvBkw3mYcP99CppEsbMZ4jSVmV/IN9r9KPxq+LZF7d3/D8FGfxrWMTYsI0SxsxniNJWZX8g32v0o/Gr4tkXt3f8PwUZ/GtYxNiwjAAAAAAAAAAAgM1zRWAMAAA==",
199 "timestampMs": "1689867116721"
200 }"#;
201
202 #[test]
203 fn new_bcs_format() {
204 serde_json::from_str::<SuiEvent>(NEW_EVENT_JSON).unwrap();
205 }
206}