1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#[proc_macro_derive(Event, attributes(indexed))]
pub fn event_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as syn::DeriveInput);
    let event_name = &input.ident;

    let indexed_fields = match input.data {
        syn::Data::Struct(syn::DataStruct { fields, .. }) => fields
            .iter()
            .filter(|f| f.attrs.iter().any(|attr| attr.path.is_ident("indexed")))
            .cloned()
            .collect::<Vec<_>>(),
        _ => {
            err!(input: "An `Event` must be a non-tuple struct.");
            return proc_macro::TokenStream::new();
        }
    };

    let impl_wrapper_ident = format_ident!("_IMPL_EVENT_FOR_{}", event_name);
    let indexed_field_idents = indexed_fields.iter().map(|f| f.ident.as_ref().unwrap());
    let event_name_topic = keccak_key(&event_name);
    let num_topics = indexed_fields.len() + 1;

    proc_macro::TokenStream::from(quote! {
        const #impl_wrapper_ident: () = {
            use mantle::reexports::*;

            impl mantle::exe::Event for #event_name  {
                fn emit(&self) {
                    let hashes = vec![
                        #(tiny_keccak::keccak256(&serde_cbor::to_vec(&self.#indexed_field_idents).unwrap())),*
                    ];
                    let mut topics: Vec<&[u8]> = Vec::with_capacity(#num_topics);
                    topics.push(&#event_name_topic);
                    topics.append(&mut hashes.iter().map(<_>::as_ref).collect());
                    mantle::backend::emit(&topics, &serde_cbor::to_vec(self).unwrap());
                }
            }
        };
    })
}