evm_coder_procedural/
lib.rs1use inflector::cases;
2use proc_macro::TokenStream;
3use quote::quote;
4use sha3::{Digest, Keccak256};
5use syn::{
6 parse_macro_input, spanned::Spanned, DeriveInput, Ident, ItemImpl, Pat, Path, PathSegment, Type,
7};
8
9mod abi_derive;
10mod solidity_interface;
11#[cfg(feature = "bondrewd")]
12mod structs;
13mod to_log;
14
15fn fn_selector_str(input: &str) -> u32 {
16 let mut hasher = Keccak256::new();
17 hasher.update(input.as_bytes());
18 let result = hasher.finalize();
19
20 let mut selector_bytes = [0; 4];
21 selector_bytes.copy_from_slice(&result[0..4]);
22
23 u32::from_be_bytes(selector_bytes)
24}
25
26#[proc_macro]
35pub fn fn_selector(input: TokenStream) -> TokenStream {
36 let input = input.to_string().replace(' ', "");
37 let selector = fn_selector_str(&input);
38
39 (quote! {
40 ::evm_coder::types::BytesFixed(u32::to_be_bytes(#selector))
41 })
42 .into()
43}
44
45fn event_selector_str(input: &str) -> [u8; 32] {
46 let mut hasher = Keccak256::new();
47 hasher.update(input.as_bytes());
48 let result = hasher.finalize();
49
50 let mut selector_bytes = [0; 32];
51 selector_bytes.copy_from_slice(&result[0..32]);
52 selector_bytes
53}
54
55#[proc_macro]
66pub fn event_topic(stream: TokenStream) -> TokenStream {
67 let input = stream.to_string().replace(' ', "");
68 let selector_bytes = event_selector_str(&input);
69
70 (quote! {
71 ::primitive_types::H256([#(
72 #selector_bytes,
73 )*])
74 })
75 .into()
76}
77
78pub(crate) fn parse_path(ty: &Type) -> syn::Result<&Path> {
79 match &ty {
80 syn::Type::Path(pat) => {
81 if let Some(qself) = &pat.qself {
82 return Err(syn::Error::new(qself.ty.span(), "no receiver expected"));
83 }
84 Ok(&pat.path)
85 }
86 _ => Err(syn::Error::new(ty.span(), "expected ty to be path")),
87 }
88}
89
90fn parse_path_segment(path: &Path) -> syn::Result<&PathSegment> {
91 if path.segments.len() != 1 {
92 return Err(syn::Error::new(
93 path.span(),
94 "expected path to have only one segment",
95 ));
96 }
97 let last_segment = &path.segments.last().unwrap();
98 Ok(last_segment)
99}
100
101fn parse_ident_from_pat(pat: &Pat) -> syn::Result<&Ident> {
102 match pat {
103 Pat::Ident(i) => Ok(&i.ident),
104 _ => Err(syn::Error::new(pat.span(), "expected pat ident")),
105 }
106}
107
108fn parse_ident_from_segment(segment: &PathSegment, allow_generics: bool) -> syn::Result<&Ident> {
109 if !segment.arguments.is_none() && !allow_generics {
110 return Err(syn::Error::new(
111 segment.arguments.span(),
112 "unexpected generic type",
113 ));
114 }
115 Ok(&segment.ident)
116}
117
118fn parse_ident_from_path(path: &Path, allow_generics: bool) -> syn::Result<&Ident> {
119 let segment = parse_path_segment(path)?;
120 parse_ident_from_segment(segment, allow_generics)
121}
122
123fn parse_ident_from_type(ty: &Type, allow_generics: bool) -> syn::Result<&Ident> {
124 let path = parse_path(ty)?;
125 parse_ident_from_path(path, allow_generics)
126}
127
128fn pascal_ident_to_call(ident: &Ident) -> Ident {
129 let name = format!("{ident}Call");
130 Ident::new(&name, ident.span())
131}
132fn snake_ident_to_pascal(ident: &Ident) -> Ident {
133 let name = ident.to_string();
134 let name = cases::pascalcase::to_pascal_case(&name);
135 Ident::new(&name, ident.span())
136}
137fn snake_ident_to_screaming(ident: &Ident) -> Ident {
138 let name = ident.to_string();
139 let name = cases::screamingsnakecase::to_screaming_snake_case(&name);
140 Ident::new(&name, ident.span())
141}
142
143#[proc_macro_attribute]
145pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream {
146 let args = parse_macro_input!(args as solidity_interface::InterfaceInfo);
147
148 let mut input: ItemImpl = match syn::parse(stream) {
149 Ok(t) => t,
150 Err(e) => return e.to_compile_error().into(),
151 };
152
153 let expanded = match solidity_interface::SolidityInterface::try_from(args, &mut input) {
154 Ok(v) => v.expand(),
155 Err(e) => e.to_compile_error(),
156 };
157
158 (quote! {
159 #input
160
161 #expanded
162 })
163 .into()
164}
165
166#[proc_macro_derive(ToLog, attributes(indexed))]
168pub fn to_log(value: TokenStream) -> TokenStream {
169 let input = parse_macro_input!(value as DeriveInput);
170
171 match to_log::Events::try_from(&input) {
172 Ok(e) => e.expand(),
173 Err(e) => e.to_compile_error(),
174 }
175 .into()
176}
177
178#[proc_macro_derive(AbiCoder)]
180pub fn abi_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
181 let ast = parse_macro_input!(input as DeriveInput);
182 let ts = match abi_derive::impl_abi_macro(&ast) {
183 Ok(e) => e,
184 Err(e) => e.to_compile_error(),
185 };
186 ts.into()
187}
188
189#[cfg(feature = "bondrewd")]
190#[proc_macro_derive(AbiCoderFlags, attributes(bondrewd,))]
192pub fn abi_flags_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
193 let ast = parse_macro_input!(input as DeriveInput);
194 let ts = match abi_derive::impl_abi_flags_macro(&ast) {
195 Ok(e) => e,
196 Err(e) => e.to_compile_error(),
197 };
198 ts.into()
199}