glin_indexer/
event_decoder.rs

1//! Event decoding utilities
2//!
3//! Provides helpers to decode runtime events into structured JSON data.
4
5use anyhow::{anyhow, Result};
6use glin_client::GlinClient;
7use scale::Decode;
8use serde::{Deserialize, Serialize};
9use subxt::events::EventDetails;
10
11/// Decoded event data
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct DecodedEvent {
14    /// Pallet name
15    pub pallet: String,
16    /// Event method name
17    pub method: String,
18    /// Decoded data as JSON
19    pub data: serde_json::Value,
20    /// Block number
21    pub block_number: u64,
22    /// Event index
23    pub event_index: u32,
24}
25
26/// Event decoder
27///
28/// Decodes runtime events into structured JSON. Supports common events
29/// with specific decoders, and falls back to hex encoding for unknown events.
30///
31/// # Example
32///
33/// ```rust,no_run
34/// use glin_client::create_client;
35/// use glin_indexer::EventDecoder;
36///
37/// #[tokio::main]
38/// async fn main() -> anyhow::Result<()> {
39///     let client = create_client("wss://testnet.glin.ai").await?;
40///     let decoder = EventDecoder::new(&client)?;
41///
42///     // Decode event
43///     // let decoded = decoder.decode(&event)?;
44///     // println!("{}", serde_json::to_string_pretty(&decoded)?);
45///     Ok(())
46/// }
47/// ```
48pub struct EventDecoder {
49    // Could store metadata for dynamic decoding if needed
50    _client: GlinClient,
51}
52
53impl EventDecoder {
54    /// Create new event decoder
55    pub fn new(client: &GlinClient) -> Result<Self> {
56        Ok(Self {
57            _client: client.clone(),
58        })
59    }
60
61    /// Decode an event to structured JSON
62    pub fn decode(&self, event: &EventDetails<subxt::PolkadotConfig>) -> Result<DecodedEvent> {
63        let pallet = event.pallet_name();
64        let method = event.variant_name();
65
66        // Get field bytes for custom decoding
67        let field_bytes = event.field_bytes();
68
69        // Decode based on event type
70        let data = match (pallet, method) {
71            ("Balances", "Transfer") => self.decode_transfer(field_bytes)?,
72            ("Contracts", "Instantiated") => self.decode_instantiated(field_bytes)?,
73            ("Contracts", "ContractEmitted") => self.decode_contract_emitted(field_bytes)?,
74            _ => {
75                // Fallback: return hex-encoded raw data
76                serde_json::json!({
77                    "raw": format!("0x{}", hex::encode(field_bytes))
78                })
79            }
80        };
81
82        Ok(DecodedEvent {
83            pallet: pallet.to_string(),
84            method: method.to_string(),
85            data,
86            block_number: 0, // Set by caller
87            event_index: event.index(),
88        })
89    }
90
91    fn decode_transfer(&self, bytes: &[u8]) -> Result<serde_json::Value> {
92        #[derive(Decode)]
93        struct Transfer {
94            from: [u8; 32],
95            to: [u8; 32],
96            amount: u128,
97        }
98
99        let transfer = Transfer::decode(&mut &bytes[..])
100            .map_err(|e| anyhow!("Failed to decode Transfer event: {}", e))?;
101
102        Ok(serde_json::json!({
103            "from": format!("0x{}", hex::encode(transfer.from)),
104            "to": format!("0x{}", hex::encode(transfer.to)),
105            "amount": transfer.amount.to_string(),
106        }))
107    }
108
109    fn decode_instantiated(&self, bytes: &[u8]) -> Result<serde_json::Value> {
110        #[derive(Decode)]
111        struct Instantiated {
112            deployer: [u8; 32],
113            contract: [u8; 32],
114        }
115
116        let inst = Instantiated::decode(&mut &bytes[..])
117            .map_err(|e| anyhow!("Failed to decode Instantiated event: {}", e))?;
118
119        Ok(serde_json::json!({
120            "deployer": format!("0x{}", hex::encode(inst.deployer)),
121            "contract": format!("0x{}", hex::encode(inst.contract)),
122        }))
123    }
124
125    fn decode_contract_emitted(&self, bytes: &[u8]) -> Result<serde_json::Value> {
126        #[derive(Decode)]
127        struct ContractEmitted {
128            contract: [u8; 32],
129            data: Vec<u8>,
130        }
131
132        let emitted = ContractEmitted::decode(&mut &bytes[..])
133            .map_err(|e| anyhow!("Failed to decode ContractEmitted event: {}", e))?;
134
135        Ok(serde_json::json!({
136            "contract": format!("0x{}", hex::encode(emitted.contract)),
137            "data": format!("0x{}", hex::encode(emitted.data)),
138        }))
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    #[test]
145    fn test_decoder_creation() {
146        // Decoder creation is tested in integration tests with real client
147    }
148}