difftreelog
refactor external generation of call parsers
in: master
4 files changed
crates/evm-coder-macros/src/lib.rsdiffbeforeafterboth1#![allow(dead_code)]23use darling::FromMeta;4use inflector::cases;5use proc_macro::TokenStream;6use quote::quote;7use sha3::{Digest, Keccak256};8use syn::{9 AttributeArgs, DeriveInput, GenericArgument, Ident, ItemTrait, Pat, Path, PathArguments,10 PathSegment, Type, parse_macro_input, spanned::Spanned,11};1213mod solidity_interface;14mod to_log;1516fn fn_selector_str(input: &str) -> u32 {17 let mut hasher = Keccak256::new();18 hasher.update(input.as_bytes());19 let result = hasher.finalize();2021 let mut selector_bytes = [0; 4];22 selector_bytes.copy_from_slice(&result[0..4]);2324 u32::from_be_bytes(selector_bytes)25}2627/// Returns solidity function selector (first 4 bytes of hash) by its28/// textual representation29///30/// ```rs31/// use evm_coder_macros::fn_selector;32///33/// assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb);34/// ```35#[proc_macro]36pub fn fn_selector(input: TokenStream) -> TokenStream {37 let input = input.to_string().replace(' ', "");38 let selector = fn_selector_str(&input);3940 (quote! {41 #selector42 })43 .into()44}4546fn event_selector_str(input: &str) -> [u8; 32] {47 let mut hasher = Keccak256::new();48 hasher.update(input.as_bytes());49 let result = hasher.finalize();5051 let mut selector_bytes = [0; 32];52 selector_bytes.copy_from_slice(&result[0..32]);53 selector_bytes54}5556/// Returns solidity topic (hash) by its textual representation57///58/// ```rs59/// use evm_coder_macros::event_topic;60///61/// assert_eq!(62/// format!("{:x}", event_topic!(Transfer(address, address, uint256))),63/// "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",64/// );65/// ```66#[proc_macro]67pub fn event_topic(stream: TokenStream) -> TokenStream {68 let input = stream.to_string().replace(' ', "");69 let selector_bytes = event_selector_str(&input);7071 (quote! {72 ::primitive_types::H256([#(73 #selector_bytes,74 )*])75 })76 .into()77}7879fn parse_path(ty: &Type) -> syn::Result<&Path> {80 match &ty {81 syn::Type::Path(pat) => {82 if let Some(qself) = &pat.qself {83 return Err(syn::Error::new(qself.ty.span(), "no receiver expected"));84 }85 Ok(&pat.path)86 }87 _ => Err(syn::Error::new(ty.span(), "expected ty to be path")),88 }89}9091fn parse_path_segment(path: &Path) -> syn::Result<&PathSegment> {92 if path.segments.len() != 1 {93 return Err(syn::Error::new(94 path.span(),95 "expected path to have only segment",96 ));97 }98 let last_segment = &path.segments.last().unwrap();99 Ok(last_segment)100}101102fn parse_ident_from_pat(pat: &Pat) -> syn::Result<&Ident> {103 match pat {104 Pat::Ident(i) => Ok(&i.ident),105 _ => Err(syn::Error::new(pat.span(), "expected pat ident")),106 }107}108109fn parse_ident_from_segment(segment: &PathSegment) -> syn::Result<&Ident> {110 if segment.arguments != PathArguments::None {111 return Err(syn::Error::new(112 segment.arguments.span(),113 "unexpected generic type",114 ));115 }116 Ok(&segment.ident)117}118119fn parse_ident_from_path(path: &Path) -> syn::Result<&Ident> {120 let segment = parse_path_segment(path)?;121 parse_ident_from_segment(segment)122}123124fn parse_ident_from_type(ty: &Type) -> syn::Result<&Ident> {125 let path = parse_path(ty)?;126 parse_ident_from_path(path)127}128129// Gets T out of Result<T>130fn parse_result_ok(ty: &Type) -> syn::Result<&Type> {131 let path = parse_path(ty)?;132 let segment = parse_path_segment(path)?;133134 if segment.ident != "Result" {135 return Err(syn::Error::new(136 ty.span(),137 "expected Result as return type (no renamed aliases allowed)",138 ));139 }140 let args = match &segment.arguments {141 PathArguments::AngleBracketed(e) => e,142 _ => {143 return Err(syn::Error::new(144 segment.arguments.span(),145 "missing Result generics",146 ))147 }148 };149150 let args = &args.args;151 let arg = args.first().unwrap();152153 let ty = match arg {154 GenericArgument::Type(ty) => ty,155 _ => {156 return Err(syn::Error::new(157 arg.span(),158 "expected first generic to be type",159 ))160 }161 };162163 Ok(ty)164}165166fn pascal_ident_to_call(ident: &Ident) -> Ident {167 let name = format!("{}Call", ident);168 Ident::new(&name, ident.span())169}170fn snake_ident_to_pascal(ident: &Ident) -> Ident {171 let name = ident.to_string();172 let name = cases::pascalcase::to_pascal_case(&name);173 Ident::new(&name, ident.span())174}175fn snake_ident_to_screaming(ident: &Ident) -> Ident {176 let name = ident.to_string();177 let name = cases::screamingsnakecase::to_screaming_snake_case(&name);178 Ident::new(&name, ident.span())179}180fn pascal_ident_to_snake_call(ident: &Ident) -> Ident {181 let name = ident.to_string();182 let name = cases::snakecase::to_snake_case(&name);183 let name = format!("call_{}", name);184 Ident::new(&name, ident.span())185}186187#[proc_macro_attribute]188pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream {189 let args = parse_macro_input!(args as AttributeArgs);190 let args = solidity_interface::InterfaceInfo::from_list(&args).unwrap();191192 let input: ItemTrait = match syn::parse(stream) {193 Ok(t) => t,194 Err(e) => return e.to_compile_error().into(),195 };196197 match solidity_interface::SolidityInterface::try_from(args, &input) {198 Ok(v) => v.expand(),199 Err(e) => e.to_compile_error(),200 }201 .into()202}203204#[proc_macro_attribute]205pub fn solidity(_args: TokenStream, stream: TokenStream) -> TokenStream {206 stream207}208209#[proc_macro_derive(ToLog, attributes(indexed))]210pub fn to_log(value: TokenStream) -> TokenStream {211 let input = parse_macro_input!(value as DeriveInput);212213 match to_log::Events::try_from(&input) {214 Ok(e) => e.expand(),215 Err(e) => e.to_compile_error(),216 }217 .into()218}1#![allow(dead_code)]23use darling::FromMeta;4use inflector::cases;5use proc_macro::TokenStream;6use quote::quote;7use sha3::{Digest, Keccak256};8use syn::{AttributeArgs, DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments, PathSegment, Type, parse_macro_input, spanned::Spanned};910mod solidity_interface;11mod to_log;1213fn fn_selector_str(input: &str) -> u32 {14 let mut hasher = Keccak256::new();15 hasher.update(input.as_bytes());16 let result = hasher.finalize();1718 let mut selector_bytes = [0; 4];19 selector_bytes.copy_from_slice(&result[0..4]);2021 u32::from_be_bytes(selector_bytes)22}2324/// Returns solidity function selector (first 4 bytes of hash) by its25/// textual representation26///27/// ```rs28/// use evm_coder_macros::fn_selector;29///30/// assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb);31/// ```32#[proc_macro]33pub fn fn_selector(input: TokenStream) -> TokenStream {34 let input = input.to_string().replace(' ', "");35 let selector = fn_selector_str(&input);3637 (quote! {38 #selector39 })40 .into()41}4243fn event_selector_str(input: &str) -> [u8; 32] {44 let mut hasher = Keccak256::new();45 hasher.update(input.as_bytes());46 let result = hasher.finalize();4748 let mut selector_bytes = [0; 32];49 selector_bytes.copy_from_slice(&result[0..32]);50 selector_bytes51}5253/// Returns solidity topic (hash) by its textual representation54///55/// ```rs56/// use evm_coder_macros::event_topic;57///58/// assert_eq!(59/// format!("{:x}", event_topic!(Transfer(address, address, uint256))),60/// "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",61/// );62/// ```63#[proc_macro]64pub fn event_topic(stream: TokenStream) -> TokenStream {65 let input = stream.to_string().replace(' ', "");66 let selector_bytes = event_selector_str(&input);6768 (quote! {69 ::primitive_types::H256([#(70 #selector_bytes,71 )*])72 })73 .into()74}7576fn parse_path(ty: &Type) -> syn::Result<&Path> {77 match &ty {78 syn::Type::Path(pat) => {79 if let Some(qself) = &pat.qself {80 return Err(syn::Error::new(qself.ty.span(), "no receiver expected"));81 }82 Ok(&pat.path)83 }84 _ => Err(syn::Error::new(ty.span(), "expected ty to be path")),85 }86}8788fn parse_path_segment(path: &Path) -> syn::Result<&PathSegment> {89 if path.segments.len() != 1 {90 return Err(syn::Error::new(91 path.span(),92 "expected path to have only segment",93 ));94 }95 let last_segment = &path.segments.last().unwrap();96 Ok(last_segment)97}9899fn parse_ident_from_pat(pat: &Pat) -> syn::Result<&Ident> {100 match pat {101 Pat::Ident(i) => Ok(&i.ident),102 _ => Err(syn::Error::new(pat.span(), "expected pat ident")),103 }104}105106fn parse_ident_from_segment(segment: &PathSegment) -> syn::Result<&Ident> {107 if segment.arguments != PathArguments::None {108 return Err(syn::Error::new(109 segment.arguments.span(),110 "unexpected generic type",111 ));112 }113 Ok(&segment.ident)114}115116fn parse_ident_from_path(path: &Path) -> syn::Result<&Ident> {117 let segment = parse_path_segment(path)?;118 parse_ident_from_segment(segment)119}120121fn parse_ident_from_type(ty: &Type) -> syn::Result<&Ident> {122 let path = parse_path(ty)?;123 parse_ident_from_path(path)124}125126// Gets T out of Result<T>127fn parse_result_ok(ty: &Type) -> syn::Result<&Type> {128 let path = parse_path(ty)?;129 let segment = parse_path_segment(path)?;130131 if segment.ident != "Result" {132 return Err(syn::Error::new(133 ty.span(),134 "expected Result as return type (no renamed aliases allowed)",135 ));136 }137 let args = match &segment.arguments {138 PathArguments::AngleBracketed(e) => e,139 _ => {140 return Err(syn::Error::new(141 segment.arguments.span(),142 "missing Result generics",143 ))144 }145 };146147 let args = &args.args;148 let arg = args.first().unwrap();149150 let ty = match arg {151 GenericArgument::Type(ty) => ty,152 _ => {153 return Err(syn::Error::new(154 arg.span(),155 "expected first generic to be type",156 ))157 }158 };159160 Ok(ty)161}162163fn pascal_ident_to_call(ident: &Ident) -> Ident {164 let name = format!("{}Call", ident);165 Ident::new(&name, ident.span())166}167fn snake_ident_to_pascal(ident: &Ident) -> Ident {168 let name = ident.to_string();169 let name = cases::pascalcase::to_pascal_case(&name);170 Ident::new(&name, ident.span())171}172fn snake_ident_to_screaming(ident: &Ident) -> Ident {173 let name = ident.to_string();174 let name = cases::screamingsnakecase::to_screaming_snake_case(&name);175 Ident::new(&name, ident.span())176}177fn pascal_ident_to_snake_call(ident: &Ident) -> Ident {178 let name = ident.to_string();179 let name = cases::snakecase::to_snake_case(&name);180 let name = format!("call_{}", name);181 Ident::new(&name, ident.span())182}183184#[proc_macro_attribute]185pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream {186 let args = parse_macro_input!(args as AttributeArgs);187 let args = solidity_interface::InterfaceInfo::from_list(&args).unwrap();188189 let input: ItemImpl = match syn::parse(stream) {190 Ok(t) => t,191 Err(e) => return e.to_compile_error().into(),192 };193194 let expanded = match solidity_interface::SolidityInterface::try_from(args, &input) {195 Ok(v) => v.expand(),196 Err(e) => e.to_compile_error(),197 };198199 (quote! {200 #input201202 #expanded203 }).into()204}205206#[proc_macro_attribute]207pub fn solidity(_args: TokenStream, stream: TokenStream) -> TokenStream {208 stream209}210211#[proc_macro_derive(ToLog, attributes(indexed))]212pub fn to_log(value: TokenStream) -> TokenStream {213 let input = parse_macro_input!(value as DeriveInput);214215 match to_log::Events::try_from(&input) {216 Ok(e) => e.expand(),217 Err(e) => e.to_compile_error(),218 }219 .into()220}crates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ b/crates/evm-coder-macros/src/solidity_interface.rs
@@ -4,7 +4,10 @@
use darling::FromMeta;
use inflector::cases;
use std::fmt::Write;
-use syn::{FnArg, Ident, ItemTrait, Meta, NestedMeta, PatType, Path, ReturnType, TraitItem, TraitItemMethod, Type, Visibility, spanned::Spanned};
+use syn::{
+ FnArg, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path, ReturnType,
+ Type, spanned::Spanned,
+};
use crate::{
fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,
@@ -51,21 +54,13 @@
fn expand_variant_call(&self) -> proc_macro2::TokenStream {
let name = &self.name;
- let snake_call_name = &self.snake_call_name;
+ let pascal_call_name = &self.pascal_call_name;
quote! {
- InternalCall::#name(call) => return self.#snake_call_name(Msg {
+ InternalCall::#name(call) => return <Self as ::evm_coder::Callable<#pascal_call_name>>::call(self, Msg {
call,
caller: c.caller,
value: c.value,
})
- }
- }
-
- fn expand_call_inner(&self) -> proc_macro2::TokenStream {
- let snake_call_name = &self.snake_call_name;
- let pascal_call_name = &self.pascal_call_name;
- quote! {
- fn #snake_call_name(&mut self, c: Msg<#pascal_call_name>) -> Result<::evm_coder::abi::AbiWriter>;
}
}
@@ -97,6 +92,7 @@
#[derive(FromMeta)]
pub struct InterfaceInfo {
+ name: Ident,
#[darling(default)]
is: IsList,
#[darling(default)]
@@ -192,7 +188,7 @@
result: Type,
}
impl Method {
- fn try_from(value: &TraitItemMethod) -> syn::Result<Self> {
+ fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {
let mut info = MethodInfo {
rename_selector: None,
};
@@ -386,36 +382,31 @@
}
pub struct SolidityInterface {
- vis: Visibility,
- name: Ident,
+ name: Box<syn::Type>,
+ ident: Ident,
info: InterfaceInfo,
methods: Vec<Method>,
- items: Vec<TraitItem>,
}
impl SolidityInterface {
- pub fn try_from(info: InterfaceInfo, value: &ItemTrait) -> syn::Result<Self> {
+ pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {
let mut methods = Vec::new();
for item in &value.items {
- match item {
- TraitItem::Method(method) => methods.push(Method::try_from(method)?),
- _ => {}
+ if let ImplItem::Method(method) = item {
+ methods.push(Method::try_from(method)?)
}
}
Ok(Self {
- vis: value.vis.clone(),
- name: value.ident.clone(),
+ name: value.self_ty.clone(),
+ ident: parse_ident_from_type(&value.self_ty)?.clone(),
info,
methods,
- items: value.items.clone(),
})
}
pub fn expand(self) -> proc_macro2::TokenStream {
- let vis = self.vis;
let name = self.name;
- let items = self.items;
- let call_name = pascal_ident_to_call(&name);
+ let call_name = pascal_ident_to_call(&self.info.name);
let call_sub = self
.info
@@ -424,13 +415,6 @@
.iter()
.chain(self.info.is.0.iter())
.map(Is::expand_call_def);
- let call_inner = self
- .info
- .inline_is
- .0
- .iter()
- .chain(self.info.is.0.iter())
- .map(Is::expand_call_inner);
let call_parse = self
.info
.inline_is
@@ -459,7 +443,7 @@
quote! {
#[derive(Debug)]
- #vis enum #call_name {
+ pub enum #call_name {
#(
#calls,
)*
@@ -486,7 +470,7 @@
)
}
}
- impl ::evm_coder::Call for #call_name {
+ impl ::evm_coder::Call for #call_name {
fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {
use ::evm_coder::abi::AbiRead;
match method_id {
@@ -500,17 +484,8 @@
)else*
return Ok(None);
}
- }
- #vis trait #name {
- #(
- #items
- )*
- #(
- #call_inner
- )*
}
- impl<T> ::evm_coder::Callable for T where T: #name {
- type Call = #call_name;
+ impl ::evm_coder::Callable<#call_name> for #name {
#[allow(unreachable_code)] // In case of no inner calls
fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {
use ::evm_coder::abi::AbiWrite;
@@ -530,7 +505,7 @@
}
Ok(writer)
}
- }
+ }
}
}
}
crates/evm-coder/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -57,9 +57,8 @@
fn parse(selector: u32, input: &mut AbiReader) -> execution::Result<Option<Self>>;
}
-pub trait Callable {
- type Call: Call;
- fn call(&mut self, call: types::Msg<Self::Call>) -> execution::Result<AbiWriter>;
+pub trait Callable<C: Call> {
+ fn call(&mut self, call: types::Msg<C>) -> execution::Result<AbiWriter>;
}
#[cfg(test)]
crates/evm-coder/tests/a.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/a.rs
+++ b/crates/evm-coder/tests/a.rs
@@ -3,24 +3,38 @@
use evm_coder::{solidity_interface, types::*, ToLog, execution::Result};
use evm_coder_macros::solidity;
-#[solidity_interface]
-trait OurInterface {
- fn fn_a(&self, input: uint256) -> Result<bool>;
+struct Impls;
+
+#[solidity_interface(name = "OurInterface")]
+impl Impls {
+ fn fn_a(&self, input: uint256) -> Result<bool> {
+ todo!()
+ }
}
-#[solidity_interface]
-trait OurInterface1 {
- fn fn_b(&self, input: uint128) -> Result<uint32>;
+#[solidity_interface(name = "OurInterface1")]
+impl Impls {
+ fn fn_b(&self, input: uint128) -> Result<uint32> {
+ todo!()
+ }
}
-#[solidity_interface(is(OurInterface), inline_is(OurInterface1), events(ERC721Log))]
-trait OurInterface2 {
+#[solidity_interface(name = "OurInterface2", is(OurInterface), inline_is(OurInterface1), events(ERC721Log))]
+impl Impls {
#[solidity(rename_selector = "fnK")]
- fn fn_c(&self, input: uint32) -> Result<uint8>;
- fn fn_d(&self, value: uint32) -> Result<uint32>;
+ fn fn_c(&self, input: uint32) -> Result<uint8> {
+ todo!()
+ }
+ fn fn_d(&self, value: uint32) -> Result<uint32> {
+ todo!()
+ }
- fn caller_sensitive(&self, caller: caller) -> Result<uint8>;
- fn payable(&mut self, value: value) -> Result<uint8>;
+ fn caller_sensitive(&self, caller: caller) -> Result<uint8> {
+ todo!()
+ }
+ fn payable(&mut self, value: value) -> Result<uint8> {
+ todo!()
+ }
}
#[derive(ToLog)]
@@ -39,18 +53,32 @@
},
}
-#[solidity_interface]
-trait ERC20 {
- fn decimals(&self) -> Result<uint8>;
- fn balance_of(&self, owner: address) -> Result<uint256>;
- fn transfer(&mut self, caller: caller, to: address, value: uint256) -> Result<bool>;
+struct ERC20;
+
+#[solidity_interface(name = "ERC20")]
+impl ERC20 {
+ fn decimals(&self) -> Result<uint8> {
+ todo!()
+ }
+ fn balance_of(&self, owner: address) -> Result<uint256> {
+ todo!()
+ }
+ fn transfer(&mut self, caller: caller, to: address, value: uint256) -> Result<bool> {
+ todo!()
+ }
fn transfer_from(
&mut self,
caller: caller,
from: address,
to: address,
value: uint256,
- ) -> Result<bool>;
- fn approve(&mut self, caller: caller, spender: address, value: uint256) -> Result<bool>;
- fn allowance(&self, owner: address, spender: address) -> Result<uint256>;
+ ) -> Result<bool> {
+ todo!()
+ }
+ fn approve(&mut self, caller: caller, spender: address, value: uint256) -> Result<bool> {
+ todo!()
+ }
+ fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
+ todo!()
+ }
}