ethers_contract_abigen/contract/
events.rs1use super::{structs::expand_event_struct, types, Context};
4use crate::util;
5use ethers_core::{
6 abi::{Event, EventExt},
7 macros::{ethers_contract_crate, ethers_core_crate},
8};
9use eyre::Result;
10use inflector::Inflector;
11use proc_macro2::{Ident, TokenStream};
12use quote::quote;
13use std::collections::BTreeMap;
14
15impl Context {
16 pub fn events_declaration(&self) -> Result<TokenStream> {
18 let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect();
19 let data_types = sorted_events
20 .values()
21 .flatten()
22 .map(|event| self.expand_event(event))
23 .collect::<Result<Vec<_>>>()?;
24
25 let events_enum_decl =
27 if data_types.len() > 1 { Some(self.expand_events_enum()) } else { None };
28
29 Ok(quote! {
30 #( #data_types )*
31
32 #events_enum_decl
33 })
34 }
35
36 pub fn event_methods(&self) -> Result<TokenStream> {
38 let sorted_events: BTreeMap<_, _> = self.abi.events.iter().collect();
39 let filter_methods = sorted_events
40 .values()
41 .flat_map(std::ops::Deref::deref)
42 .map(|event| self.expand_filter(event));
43
44 let events_method = self.expand_events_method();
45
46 Ok(quote! {
47 #( #filter_methods )*
48
49 #events_method
50 })
51 }
52
53 fn expand_events_enum(&self) -> TokenStream {
55 let variants = self
56 .abi
57 .events
58 .values()
59 .flatten()
60 .map(|e| {
61 event_struct_name(&e.name, self.event_aliases.get(&e.abi_signature()).cloned())
62 })
63 .collect::<Vec<_>>();
64
65 let enum_name = self.expand_event_enum_name();
66
67 let mut derives = self.expand_extra_derives();
68 let params =
69 self.abi.events.values().flatten().flat_map(|err| &err.inputs).map(|param| ¶m.kind);
70 util::derive_builtin_traits(params, &mut derives, false, true);
71
72 let ethers_core = ethers_core_crate();
73 let ethers_contract = ethers_contract_crate();
74
75 quote! {
76 #[doc = "Container type for all of the contract's events"]
77 #[derive(Clone, #ethers_contract::EthAbiType, #derives)]
78 pub enum #enum_name {
79 #( #variants(#variants), )*
80 }
81
82 impl #ethers_contract::EthLogDecode for #enum_name {
83 fn decode_log(log: &#ethers_core::abi::RawLog) -> ::core::result::Result<Self, #ethers_core::abi::Error> {
84 #(
85 if let Ok(decoded) = #variants::decode_log(log) {
86 return Ok(#enum_name::#variants(decoded))
87 }
88 )*
89 Err(#ethers_core::abi::Error::InvalidData)
90 }
91 }
92
93 impl ::core::fmt::Display for #enum_name {
94 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
95 match self {
96 #(
97 Self::#variants(element) => ::core::fmt::Display::fmt(element, f),
98 )*
99 }
100 }
101 }
102
103 #(
104 impl ::core::convert::From<#variants> for #enum_name {
105 fn from(value: #variants) -> Self {
106 Self::#variants(value)
107 }
108 }
109 )*
110 }
111 }
112
113 fn expand_event_enum_name(&self) -> Ident {
115 util::ident(&format!("{}Events", self.contract_ident))
116 }
117
118 fn expand_events_method(&self) -> Option<TokenStream> {
120 let sorted_events: BTreeMap<_, _> = self.abi.events.clone().into_iter().collect();
121
122 let mut iter = sorted_events.values().flatten();
123 let ethers_contract = ethers_contract_crate();
124
125 if let Some(event) = iter.next() {
126 let ty = if iter.next().is_some() {
127 self.expand_event_enum_name()
128 } else {
129 event_struct_name(
130 &event.name,
131 self.event_aliases.get(&event.abi_signature()).cloned(),
132 )
133 };
134
135 Some(quote! {
136 pub fn events(&self) -> #ethers_contract::builders::Event<
138 ::std::sync::Arc<M>,
139 M,
140 #ty,
141 > {
142 self.0.event_with_filter(::core::default::Default::default())
143 }
144 })
145 } else {
146 None
147 }
148 }
149
150 fn expand_filter(&self, event: &Event) -> TokenStream {
152 let name = &event.name;
153 let sig = event.abi_signature();
154 let alias = self.event_aliases.get(&sig).cloned();
155
156 let function_name = {
158 let name = if let Some(ref id) = alias {
159 id.to_string().to_snake_case()
160 } else {
161 name.to_snake_case()
162 };
163 util::safe_ident(&format!("{name}_filter"))
164 };
165 let struct_name = event_struct_name(name, alias);
166
167 let doc_str = format!("Gets the contract's `{name}` event");
168
169 let ethers_contract = ethers_contract_crate();
170
171 quote! {
172 #[doc = #doc_str]
173 pub fn #function_name(&self) -> #ethers_contract::builders::Event<
174 ::std::sync::Arc<M>,
175 M,
176 #struct_name
177 > {
178 self.0.event()
179 }
180 }
181 }
182
183 fn expand_event(&self, event: &Event) -> Result<TokenStream> {
187 let name = &event.name;
188 let abi_signature = event.abi_signature();
189 let alias = self.event_aliases.get(&abi_signature).cloned();
190
191 let struct_name = event_struct_name(name, alias);
192
193 let fields = types::expand_event_inputs(event, &self.internal_structs)?;
194 let all_anonymous_fields = event.inputs.iter().all(|input| input.name.is_empty());
196 let data_type_definition = expand_event_struct(&struct_name, &fields, all_anonymous_fields);
197
198 let mut derives = self.expand_extra_derives();
199 let params = event.inputs.iter().map(|param| ¶m.kind);
200 util::derive_builtin_traits(params, &mut derives, true, true);
201
202 let ethers_contract = ethers_contract_crate();
203
204 Ok(quote! {
205 #[derive(Clone, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #derives)]
206 #[ethevent(name = #name, abi = #abi_signature)]
207 pub #data_type_definition
208 })
209 }
210}
211
212fn event_struct_name(event_name: &str, alias: Option<Ident>) -> Ident {
214 let name = if let Some(id) = alias {
217 format!("{}Filter", id.to_string().to_pascal_case())
218 } else {
219 format!("{}Filter", event_name.to_pascal_case())
220 };
221 util::ident(&name)
222}
223
224pub(crate) fn event_struct_alias(event_name: &str) -> Ident {
226 util::ident(&event_name.to_pascal_case())
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use crate::Abigen;
233 use ethers_core::abi::{EventParam, ParamType};
234
235 fn test_context() -> Context {
236 Context::from_abigen(Abigen::new("TestToken", "[]").unwrap()).unwrap()
237 }
238
239 fn test_context_with_alias(sig: &str, alias: &str) -> Context {
240 Context::from_abigen(Abigen::new("TestToken", "[]").unwrap().add_event_alias(sig, alias))
241 .unwrap()
242 }
243
244 #[test]
245 fn expand_transfer_filter_with_alias() {
246 let event = Event {
247 name: "Transfer".into(),
248 inputs: vec![
249 EventParam { name: "from".into(), kind: ParamType::Address, indexed: true },
250 EventParam { name: "to".into(), kind: ParamType::Address, indexed: true },
251 EventParam { name: "amount".into(), kind: ParamType::Uint(256), indexed: false },
252 ],
253 anonymous: false,
254 };
255 let sig = "Transfer(address,address,uint256)";
256 let cx = test_context_with_alias(sig, "TransferEvent");
257 #[rustfmt::skip]
258 assert_quote!(cx.expand_filter(&event), {
259 #[doc = "Gets the contract's `Transfer` event"]
260 pub fn transfer_event_filter(
261 &self
262 ) -> ::ethers_contract::builders::Event<::std::sync::Arc<M>, M, TransferEventFilter>
263 {
264 self.0.event()
265 }
266 });
267 }
268
269 #[test]
270 fn expand_transfer_filter() {
271 let event = Event {
272 name: "Transfer".into(),
273 inputs: vec![
274 EventParam { name: "from".into(), kind: ParamType::Address, indexed: true },
275 EventParam { name: "to".into(), kind: ParamType::Address, indexed: true },
276 EventParam { name: "amount".into(), kind: ParamType::Uint(256), indexed: false },
277 ],
278 anonymous: false,
279 };
280 let cx = test_context();
281 #[rustfmt::skip]
282 assert_quote!(cx.expand_filter(&event), {
283 #[doc = "Gets the contract's `Transfer` event"]
284 pub fn transfer_filter(
285 &self
286 ) -> ::ethers_contract::builders::Event<::std::sync::Arc<M>, M, TransferFilter>
287 {
288 self.0.event()
289 }
290 });
291 }
292
293 #[test]
294 fn expand_data_struct_value() {
295 let event = Event {
296 name: "Foo".into(),
297 inputs: vec![
298 EventParam { name: "a".into(), kind: ParamType::Bool, indexed: false },
299 EventParam { name: String::new(), kind: ParamType::Address, indexed: false },
300 ],
301 anonymous: false,
302 };
303
304 let cx = test_context();
305 let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
306 let name = event_struct_name(&event.name, None);
307 let definition = expand_event_struct(&name, ¶ms, false);
308
309 assert_quote!(definition, {
310 struct FooFilter {
311 pub a: bool,
312 pub p1: ::ethers_core::types::Address,
313 }
314 });
315 }
316
317 #[test]
318 fn expand_data_struct_with_alias() {
319 let event = Event {
320 name: "Foo".into(),
321 inputs: vec![
322 EventParam { name: "a".into(), kind: ParamType::Bool, indexed: false },
323 EventParam { name: String::new(), kind: ParamType::Address, indexed: false },
324 ],
325 anonymous: false,
326 };
327
328 let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
329 let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
330 let alias = Some(util::ident("FooAliased"));
331 let name = event_struct_name(&event.name, alias);
332 let definition = expand_event_struct(&name, ¶ms, false);
333
334 assert_quote!(definition, {
335 struct FooAliasedFilter {
336 pub a: bool,
337 pub p1: ::ethers_core::types::Address,
338 }
339 });
340 }
341
342 #[test]
343 fn expand_data_tuple_value() {
344 let event = Event {
345 name: "Foo".into(),
346 inputs: vec![
347 EventParam { name: String::new(), kind: ParamType::Bool, indexed: false },
348 EventParam { name: String::new(), kind: ParamType::Address, indexed: false },
349 ],
350 anonymous: false,
351 };
352
353 let cx = test_context();
354 let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
355 let name = event_struct_name(&event.name, None);
356 let definition = expand_event_struct(&name, ¶ms, true);
357
358 assert_quote!(definition, {
359 struct FooFilter(pub bool, pub ::ethers_core::types::Address);
360 });
361 }
362
363 #[test]
364 fn expand_data_tuple_value_with_alias() {
365 let event = Event {
366 name: "Foo".into(),
367 inputs: vec![
368 EventParam { name: String::new(), kind: ParamType::Bool, indexed: false },
369 EventParam { name: String::new(), kind: ParamType::Address, indexed: false },
370 ],
371 anonymous: false,
372 };
373
374 let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
375 let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
376 let alias = Some(util::ident("FooAliased"));
377 let name = event_struct_name(&event.name, alias);
378 let definition = expand_event_struct(&name, ¶ms, true);
379
380 assert_quote!(definition, {
381 struct FooAliasedFilter(pub bool, pub ::ethers_core::types::Address);
382 });
383 }
384}