difftreelog
feat define new execution traits
in: master
5 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<&Ident> {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 parse_ident_from_type(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}186187fn format_ty(ty: &Ident) -> String {188 if ty == "string" {189 format!("{} memory", ty)190 } else {191 ty.to_string()192 }193}194195#[proc_macro_attribute]196pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream {197 let args = parse_macro_input!(args as AttributeArgs);198 let args = solidity_interface::InterfaceInfo::from_list(&args).unwrap();199200 let input: ItemTrait = match syn::parse(stream) {201 Ok(t) => t,202 Err(e) => return e.to_compile_error().into(),203 };204205 match solidity_interface::SolidityInterface::try_from(args, &input) {206 Ok(v) => v.expand(),207 Err(e) => e.to_compile_error(),208 }209 .into()210}211212#[proc_macro_attribute]213pub fn solidity(_args: TokenStream, stream: TokenStream) -> TokenStream {214 stream215}216217#[proc_macro_derive(ToLog, attributes(indexed))]218pub fn to_log(value: TokenStream) -> TokenStream {219 let input = parse_macro_input!(value as DeriveInput);220221 match to_log::Events::try_from(&input) {222 Ok(e) => e.expand(),223 Err(e) => e.to_compile_error(),224 }225 .into()226}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,13 +4,10 @@
use darling::FromMeta;
use inflector::cases;
use std::fmt::Write;
-use syn::{
- FnArg, Ident, ItemTrait, Meta, NestedMeta, PatType, Path, ReturnType, TraitItem,
- TraitItemMethod, Visibility, spanned::Spanned,
-};
+use syn::{FnArg, Ident, ItemTrait, Meta, NestedMeta, PatType, Path, ReturnType, TraitItem, TraitItemMethod, Type, Visibility, spanned::Spanned};
use crate::{
- fn_selector_str, format_ty, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,
+ fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,
parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,
snake_ident_to_screaming,
};
@@ -68,7 +65,7 @@
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>) -> ::core::result::Result<::evm_coder::abi::AbiWriter, Self::Error>;
+ fn #snake_call_name(&mut self, c: Msg<#pascal_call_name>) -> Result<::evm_coder::abi::AbiWriter>;
}
}
@@ -173,11 +170,6 @@
}
}
}
-
- fn solidity_def(&self) -> String {
- assert!(!self.is_special());
- format!("{} {}", format_ty(&self.ty), self.name)
- }
}
#[derive(PartialEq)]
@@ -197,7 +189,7 @@
args: Vec<MethodArg>,
has_normal_args: bool,
mutability: Mutability,
- result: Ident,
+ result: Type,
}
impl Method {
fn try_from(value: &TraitItemMethod) -> syn::Result<Self> {
@@ -388,29 +380,8 @@
)*
)?;
(&result).abi_write(&mut writer);
- }
- }
- }
-
- fn solidity_def(&self) -> String {
- let mut out = format!("function {}(", self.camel_name);
- for (i, arg) in self.args.iter().filter(|a| !a.is_special()).enumerate() {
- if i != 0 {
- out.push_str(", ");
}
- out.push_str(&arg.solidity_def());
- }
- out.push(')');
- match self.mutability {
- Mutability::Mutable => {}
- Mutability::View => write!(out, " view").unwrap(),
- Mutability::Pure => write!(out, " pure").unwrap(),
}
- if self.result != "void" {
- write!(out, " returns ({})", format_ty(&self.result)).unwrap();
- }
- out.push(';');
- out
}
}
@@ -423,26 +394,14 @@
}
impl SolidityInterface {
pub fn try_from(info: InterfaceInfo, value: &ItemTrait) -> syn::Result<Self> {
- let mut found_error = false;
let mut methods = Vec::new();
for item in &value.items {
match item {
- TraitItem::Type(ty) => {
- if ty.ident == "Error" {
- found_error = true;
- }
- }
TraitItem::Method(method) => methods.push(Method::try_from(method)?),
_ => {}
}
}
- if !found_error {
- return Err(syn::Error::new(
- value.span(),
- "expected associated type called Error, which should implement From<&str>",
- ));
- }
Ok(Self {
vis: value.vis.clone(),
name: value.ident.clone(),
@@ -512,19 +471,6 @@
#(
#consts
)*
- pub fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::abi::Result<Option<Self>> {
- use ::evm_coder::abi::AbiRead;
- match method_id {
- #(
- #parsers,
- )*
- _ => {},
- }
- #(
- #call_parse
- )else*
- return Ok(None);
- }
pub const fn interface_id() -> u32 {
let mut interface_id = 0;
#(#interface_id)*
@@ -540,6 +486,21 @@
)
}
}
+ 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 {
+ #(
+ #parsers,
+ )*
+ _ => {},
+ }
+ #(
+ #call_parse
+ )else*
+ return Ok(None);
+ }
+ }
#vis trait #name {
#(
#items
@@ -547,8 +508,11 @@
#(
#call_inner
)*
+ }
+ impl<T> ::evm_coder::Callable for T where T: #name {
+ type Call = #call_name;
#[allow(unreachable_code)] // In case of no inner calls
- fn call(&mut self, c: Msg<#call_name>) -> ::core::result::Result<::evm_coder::abi::AbiWriter, Self::Error> {
+ fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {
use ::evm_coder::abi::AbiWrite;
type InternalCall = #call_name;
match c.call {
@@ -566,7 +530,7 @@
}
Ok(writer)
}
- }
+ }
}
}
}
crates/evm-coder/Cargo.tomldiffbeforeafterboth--- a/crates/evm-coder/Cargo.toml
+++ b/crates/evm-coder/Cargo.toml
@@ -8,6 +8,8 @@
primitive-types = { version = "0.9", default-features = false }
hex-literal = "0.3"
ethereum = { version = "0.7.1", default-features = false }
+evm-core = { git = "https://github.com/usetech-llc/evm.git", branch="precompile-output-parachain" }
+impl-trait-for-tuples = "0.2.1"
[dev-dependencies]
hex = "0.4.3"
crates/evm-coder/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -2,10 +2,13 @@
#[cfg(not(feature = "std"))]
extern crate alloc;
+use abi::{AbiReader, AbiWriter};
pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, ToLog};
pub mod abi;
pub mod events;
pub use events::ToLog;
+pub mod execution;
+pub mod solidity;
/// Solidity type definitions
pub mod types {
@@ -50,6 +53,15 @@
}
}
+pub trait Call: Sized {
+ 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>;
+}
+
#[cfg(test)]
mod tests {
use super::*;
crates/evm-coder/tests/a.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/a.rs
+++ b/crates/evm-coder/tests/a.rs
@@ -1,29 +1,26 @@
#![allow(dead_code)] // This test only checks that macros is not panicking
-use evm_coder::{solidity_interface, types::*, ToLog};
+use evm_coder::{solidity_interface, types::*, ToLog, execution::Result};
use evm_coder_macros::solidity;
#[solidity_interface]
trait OurInterface {
- type Error;
- fn fn_a(&self, input: uint256) -> Result<bool, Self::Error>;
+ fn fn_a(&self, input: uint256) -> Result<bool>;
}
#[solidity_interface]
trait OurInterface1 {
- type Error;
- fn fn_b(&self, input: uint128) -> Result<uint32, Self::Error>;
+ fn fn_b(&self, input: uint128) -> Result<uint32>;
}
#[solidity_interface(is(OurInterface), inline_is(OurInterface1), events(ERC721Log))]
trait OurInterface2 {
- type Error;
#[solidity(rename_selector = "fnK")]
- fn fn_c(&self, input: uint32) -> Result<uint8, Self::Error>;
- fn fn_d(&self, value: uint32) -> Result<uint32, Self::Error>;
+ fn fn_c(&self, input: uint32) -> Result<uint8>;
+ fn fn_d(&self, value: uint32) -> Result<uint32>;
- fn caller_sensitive(&self, caller: caller) -> Result<uint8, Self::Error>;
- fn payable(&mut self, value: value) -> Result<uint8, Self::Error>;
+ fn caller_sensitive(&self, caller: caller) -> Result<uint8>;
+ fn payable(&mut self, value: value) -> Result<uint8>;
}
#[derive(ToLog)]
@@ -44,28 +41,16 @@
#[solidity_interface]
trait ERC20 {
- type Error;
-
- fn decimals(&self) -> Result<uint8, Self::Error>;
- fn balance_of(&self, owner: address) -> Result<uint256, Self::Error>;
- fn transfer(
- &mut self,
- caller: caller,
- to: address,
- value: uint256,
- ) -> Result<bool, Self::Error>;
+ 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>;
fn transfer_from(
&mut self,
caller: caller,
from: address,
to: address,
value: uint256,
- ) -> Result<bool, Self::Error>;
- fn approve(
- &mut self,
- caller: caller,
- spender: address,
- value: uint256,
- ) -> Result<bool, Self::Error>;
- fn allowance(&self, owner: address, spender: address) -> Result<uint256, Self::Error>;
+ ) -> Result<bool>;
+ fn approve(&mut self, caller: caller, spender: address, value: uint256) -> Result<bool>;
+ fn allowance(&self, owner: address, spender: address) -> Result<uint256>;
}