ethcontract/contract/event/
data.rs

1//! Module contains code for parsing and manipulating event data.
2use crate::{errors::ExecutionError, tokens::Tokenize};
3use ethcontract_common::abi::{Event as AbiEvent, RawLog as AbiRawLog, Token};
4use web3::types::{Log, H160, H256};
5
6/// A contract event
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub struct Event<T> {
9    /// The decoded log data.
10    pub data: T,
11    /// The additional metadata for the event. Note that this is not always
12    /// available if these logs are pending. This can happen if the `to_block`
13    /// option was set to `BlockNumber::Pending`.
14    pub meta: Option<EventMetadata>,
15}
16
17/// A contract event from an event stream.
18///
19/// This is similar to `Event`s except the event may either be added (in case a
20/// new block is mined) or removed (in case of re-orgs when blocks are removed).
21pub type StreamEvent<T> = Event<EventStatus<T>>;
22
23/// A type representing a contract event that was either added or removed. Note
24/// that this type intentionally an enum so that the handling of removed events
25/// is made more explicit.
26#[derive(Clone, Debug, Eq, PartialEq)]
27pub enum EventStatus<T> {
28    /// A new event was received.
29    Added(T),
30    /// A previously mined event was removed as a result of a re-org.
31    Removed(T),
32}
33
34impl<T> Event<T> {
35    /// Creates an event from a log given a mapping function.
36    pub(crate) fn from_past_log<E, F>(log: Log, f: F) -> Result<Self, ExecutionError>
37    where
38        F: FnOnce(RawLog) -> Result<T, E>,
39        ExecutionError: From<E>,
40    {
41        if log.removed == Some(true) {
42            return Err(ExecutionError::RemovedLog(Box::new(log)));
43        }
44
45        let meta = EventMetadata::from_log(&log);
46        let raw = RawLog::from(log);
47        let data = f(raw)?;
48
49        Ok(Event { data, meta })
50    }
51}
52
53impl<T> Event<EventStatus<T>> {
54    /// Creates an event from a log given a mapping function.
55    pub(crate) fn from_streamed_log<E, F>(log: Log, f: F) -> Result<Self, ExecutionError>
56    where
57        F: FnOnce(RawLog) -> Result<T, E>,
58        ExecutionError: From<E>,
59    {
60        let removed = log.removed == Some(true);
61        let meta = EventMetadata::from_log(&log);
62        let raw = RawLog::from(log);
63        let inner_data = f(raw)?;
64
65        let data = if removed {
66            EventStatus::Removed(inner_data)
67        } else {
68            EventStatus::Added(inner_data)
69        };
70
71        Ok(Event { data, meta })
72    }
73
74    /// Get a reference the underlying event data regardless of whether the
75    /// event was added or removed.
76    pub fn inner_data(&self) -> &T {
77        match &self.data {
78            EventStatus::Added(value) => value,
79            EventStatus::Removed(value) => value,
80        }
81    }
82
83    /// Gets a bool representing if the event was added.
84    pub fn is_added(&self) -> bool {
85        matches!(&self.data, EventStatus::Added(_))
86    }
87
88    /// Gets a bool representing if the event was removed.
89    pub fn is_removed(&self) -> bool {
90        matches!(&self.data, EventStatus::Removed(_))
91    }
92
93    /// Get the underlying event data if the event was added, `None` otherwise.
94    pub fn added(self) -> Option<T> {
95        match self.data {
96            EventStatus::Added(value) => Some(value),
97            EventStatus::Removed(_) => None,
98        }
99    }
100
101    /// Get the underlying event data if the event was removed, `None`
102    /// otherwise.
103    pub fn removed(self) -> Option<T> {
104        match self.data {
105            EventStatus::Removed(value) => Some(value),
106            EventStatus::Added(_) => None,
107        }
108    }
109
110    /// Maps the inner data of an event into some other data.
111    pub fn map<U, F>(self, f: F) -> StreamEvent<U>
112    where
113        F: FnOnce(T) -> U,
114    {
115        Event {
116            data: match self.data {
117                EventStatus::Added(inner) => EventStatus::Added(f(inner)),
118                EventStatus::Removed(inner) => EventStatus::Removed(f(inner)),
119            },
120            meta: self.meta,
121        }
122    }
123}
124
125/// Additional metadata from the log for the event.
126#[derive(Clone, Debug, Default, Eq, PartialEq)]
127pub struct EventMetadata {
128    /// From which this event originated from
129    pub address: H160,
130    /// The hash of the block where the log was produced.
131    pub block_hash: H256,
132    /// The number of the block where the log was produced.
133    pub block_number: u64,
134    /// The hash of the transaction this log belongs to.
135    pub transaction_hash: H256,
136    /// The block index of the transaction this log belongs to.
137    pub transaction_index: usize,
138    /// The index of the log in the block.
139    pub log_index: usize,
140    /// The log index in the transaction this log belongs to. This property is
141    /// non-standard.
142    pub transaction_log_index: Option<usize>,
143    /// The log type. Note that this property is non-standard but is supported
144    /// by Parity nodes.
145    pub log_type: Option<String>,
146}
147
148impl EventMetadata {
149    fn from_log(log: &Log) -> Option<Self> {
150        Some(EventMetadata {
151            address: log.address,
152            block_hash: log.block_hash?,
153            block_number: log.block_number?.as_u64(),
154            transaction_hash: log.transaction_hash?,
155            transaction_index: log.transaction_index?.as_usize(),
156            log_index: log.log_index?.as_usize(),
157            transaction_log_index: log.transaction_log_index.map(|index| index.as_usize()),
158            log_type: log.log_type.clone(),
159        })
160    }
161}
162
163/// Raw log topics and data for a contract event.
164#[derive(Clone, Debug, Eq, PartialEq)]
165pub struct RawLog {
166    /// The raw 32-byte topics.
167    pub topics: Vec<H256>,
168    /// The raw non-indexed data attached to an event.
169    pub data: Vec<u8>,
170}
171
172impl RawLog {
173    /// Decode raw log data into a tokenizable for a matching event ABI entry.
174    pub fn decode<D>(self, event: &AbiEvent) -> Result<D, ExecutionError>
175    where
176        D: Tokenize,
177    {
178        let event_log = event.parse_log(AbiRawLog {
179            topics: self.topics,
180            data: self.data,
181        })?;
182
183        let tokens = event_log
184            .params
185            .into_iter()
186            .map(|param| param.value)
187            .collect::<Vec<_>>();
188        let data = D::from_token(Token::Tuple(tokens))?;
189
190        Ok(data)
191    }
192}
193
194impl From<Log> for RawLog {
195    fn from(log: Log) -> Self {
196        RawLog {
197            topics: log.topics,
198            data: log.data.0,
199        }
200    }
201}
202
203/// Trait for parsing a transaction log into an some event data when the
204/// expected event type is not known.
205pub trait ParseLog: Sized + Send + Sync {
206    /// Create a new instance by parsing raw log data.
207    fn parse_log(log: RawLog) -> Result<Self, ExecutionError>;
208}
209
210impl ParseLog for RawLog {
211    fn parse_log(log: RawLog) -> Result<Self, ExecutionError> {
212        Ok(log)
213    }
214}