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