refactor(abigen): replace ethabi::Reader (#1417)
* feat: add human readable function parser * clippy fix * revert bad clippy * refactor(abigen): replace ethabi::Reader * fix: add token to tuple token parsing
This commit is contained in:
parent
94c7559ab5
commit
659ac061b2
|
@ -1,25 +1,21 @@
|
||||||
//! Methods for expanding structs
|
//! Methods for expanding structs
|
||||||
use std::collections::{HashMap, VecDeque};
|
|
||||||
|
|
||||||
use eyre::{eyre, Result};
|
|
||||||
use inflector::Inflector;
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
use ethers_core::{
|
|
||||||
abi::{
|
|
||||||
param_type::Reader,
|
|
||||||
struct_def::{FieldDeclaration, FieldType, StructFieldType, StructType},
|
|
||||||
ParamType, SolStruct,
|
|
||||||
},
|
|
||||||
macros::ethers_contract_crate,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
contract::{types, Context},
|
contract::{types, Context},
|
||||||
rawabi::{Component, RawAbi},
|
rawabi::{Component, RawAbi},
|
||||||
util,
|
util,
|
||||||
};
|
};
|
||||||
|
use ethers_core::{
|
||||||
|
abi::{
|
||||||
|
struct_def::{FieldDeclaration, FieldType, StructFieldType, StructType},
|
||||||
|
HumanReadableParser, ParamType, SolStruct,
|
||||||
|
},
|
||||||
|
macros::ethers_contract_crate,
|
||||||
|
};
|
||||||
|
use eyre::{eyre, Result};
|
||||||
|
use inflector::Inflector;
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
/// Generate corresponding types for structs parsed from a human readable ABI
|
/// Generate corresponding types for structs parsed from a human readable ABI
|
||||||
|
@ -442,7 +438,11 @@ fn insert_structs(structs: &mut HashMap<String, SolStruct>, tuple: &Component) {
|
||||||
if let Some(fields) = tuple
|
if let Some(fields) = tuple
|
||||||
.components
|
.components
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| Reader::read(&f.type_field).ok().and_then(|kind| field(structs, f, kind)))
|
.map(|f| {
|
||||||
|
HumanReadableParser::parse_type(&f.type_field)
|
||||||
|
.ok()
|
||||||
|
.and_then(|kind| field(structs, f, kind))
|
||||||
|
})
|
||||||
.collect::<Option<Vec<_>>>()
|
.collect::<Option<Vec<_>>>()
|
||||||
{
|
{
|
||||||
let s = SolStruct { name: ident.to_string(), fields };
|
let s = SolStruct { name: ident.to_string(), fields };
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
//! Helper functions for deriving `EthCall`
|
//! Helper functions for deriving `EthCall`
|
||||||
|
|
||||||
|
use crate::{abi_ty, utils};
|
||||||
|
use ethers_core::{
|
||||||
|
abi::{Function, FunctionExt, HumanReadableParser},
|
||||||
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
|
};
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse::Error, spanned::Spanned as _, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
|
use syn::{parse::Error, spanned::Spanned as _, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
|
||||||
|
|
||||||
use ethers_core::{
|
|
||||||
abi::{param_type::Reader, AbiParser, Function, FunctionExt, Param, ParamType},
|
|
||||||
macros::{ethers_contract_crate, ethers_core_crate},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{abi_ty, utils};
|
|
||||||
|
|
||||||
/// Generates the `ethcall` trait support
|
/// Generates the `ethcall` trait support
|
||||||
pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
|
pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
|
||||||
let attributes = match parse_call_attributes(&input) {
|
let attributes = match parse_call_attributes(&input) {
|
||||||
|
@ -22,43 +20,16 @@ pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
|
||||||
attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
|
attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
|
||||||
|
|
||||||
let mut function = if let Some((src, span)) = attributes.abi {
|
let mut function = if let Some((src, span)) = attributes.abi {
|
||||||
|
let raw_function_sig = src.trim_start_matches("function ").trim_start();
|
||||||
// try to parse as solidity function
|
// try to parse as solidity function
|
||||||
if let Ok(fun) = parse_function(&src) {
|
if let Ok(fun) = HumanReadableParser::parse_function(&src) {
|
||||||
fun
|
fun
|
||||||
} else {
|
|
||||||
// try as tuple
|
|
||||||
let raw_function_signature = src.trim_start_matches("function ").trim_start();
|
|
||||||
if let Some(inputs) =
|
|
||||||
Reader::read(raw_function_signature.trim_start_matches(&function_call_name))
|
|
||||||
.ok()
|
|
||||||
.and_then(|param| match param {
|
|
||||||
ParamType::Tuple(params) => Some(
|
|
||||||
params
|
|
||||||
.into_iter()
|
|
||||||
.map(|kind| Param {
|
|
||||||
name: "".to_string(),
|
|
||||||
kind,
|
|
||||||
internal_type: None,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
{
|
|
||||||
#[allow(deprecated)]
|
|
||||||
Function {
|
|
||||||
name: function_call_name.clone(),
|
|
||||||
inputs,
|
|
||||||
outputs: vec![],
|
|
||||||
constant: None,
|
|
||||||
state_mutability: Default::default(),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// try to determine the abi by using its fields at runtime
|
// try to determine the abi by using its fields at runtime
|
||||||
return match derive_trait_impls_with_abi_type(
|
return match derive_trait_impls_with_abi_type(
|
||||||
&input,
|
&input,
|
||||||
&function_call_name,
|
&function_call_name,
|
||||||
Some(raw_function_signature),
|
Some(raw_function_sig),
|
||||||
) {
|
) {
|
||||||
Ok(derived) => derived,
|
Ok(derived) => derived,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -67,7 +38,6 @@ pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// try to determine the abi by using its fields at runtime
|
// try to determine the abi by using its fields at runtime
|
||||||
return match derive_trait_impls_with_abi_type(&input, &function_call_name, None) {
|
return match derive_trait_impls_with_abi_type(&input, &function_call_name, None) {
|
||||||
|
@ -287,15 +257,3 @@ fn parse_call_attributes(input: &DeriveInput) -> Result<EthCallAttributes, Token
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_function(abi: &str) -> Result<Function, String> {
|
|
||||||
let abi = if !abi.trim_start().starts_with("function ") {
|
|
||||||
format!("function {}", abi)
|
|
||||||
} else {
|
|
||||||
abi.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
AbiParser::default()
|
|
||||||
.parse_function(&abi)
|
|
||||||
.map_err(|err| format!("Failed to parse the function ABI: {:?}", err))
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{param_type::Reader, AbiParser, Event, EventExt, EventParam, ParamType},
|
abi::{Event, EventExt, EventParam, HumanReadableParser},
|
||||||
macros::{ethers_contract_crate, ethers_core_crate},
|
macros::{ethers_contract_crate, ethers_core_crate},
|
||||||
};
|
};
|
||||||
use hex::FromHex;
|
use hex::FromHex;
|
||||||
|
@ -32,24 +32,8 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
|
||||||
|
|
||||||
let mut event = if let Some((src, span)) = attributes.abi {
|
let mut event = if let Some((src, span)) = attributes.abi {
|
||||||
// try to parse as solidity event
|
// try to parse as solidity event
|
||||||
if let Ok(event) = parse_event(&src) {
|
if let Ok(event) = HumanReadableParser::parse_event(&src) {
|
||||||
event
|
event
|
||||||
} else {
|
|
||||||
// try as tuple
|
|
||||||
if let Some(inputs) = Reader::read(
|
|
||||||
src.trim_start_matches("event ").trim_start().trim_start_matches(&event_name),
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
.and_then(|param| match param {
|
|
||||||
ParamType::Tuple(params) => Some(
|
|
||||||
params
|
|
||||||
.into_iter()
|
|
||||||
.map(|kind| EventParam { name: "".to_string(), indexed: false, kind })
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
_ => None,
|
|
||||||
}) {
|
|
||||||
Event { name: event_name.clone(), inputs, anonymous: false }
|
|
||||||
} else {
|
} else {
|
||||||
match src.parse::<Source>().and_then(|s| s.get()) {
|
match src.parse::<Source>().and_then(|s| s.get()) {
|
||||||
Ok(abi) => {
|
Ok(abi) => {
|
||||||
|
@ -60,7 +44,7 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
|
||||||
// this could be mitigated by getting the ABI of each non elementary type
|
// this could be mitigated by getting the ABI of each non elementary type
|
||||||
// at runtime and computing the the signature as
|
// at runtime and computing the the signature as
|
||||||
// `static Lazy::...`
|
// `static Lazy::...`
|
||||||
match parse_event(&abi) {
|
match HumanReadableParser::parse_event(&abi) {
|
||||||
Ok(event) => event,
|
Ok(event) => event,
|
||||||
Err(err) => return Error::new(span, err).to_compile_error(),
|
Err(err) => return Error::new(span, err).to_compile_error(),
|
||||||
}
|
}
|
||||||
|
@ -68,7 +52,6 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
|
||||||
Err(err) => return Error::new(span, err).to_compile_error(),
|
Err(err) => return Error::new(span, err).to_compile_error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// try to determine the abi from the fields
|
// try to determine the abi from the fields
|
||||||
match derive_abi_event_from_fields(&input) {
|
match derive_abi_event_from_fields(&input) {
|
||||||
|
@ -362,17 +345,6 @@ fn parse_field_attributes(field: &Field) -> Result<(Option<String>, bool), Error
|
||||||
Ok((topic_name, indexed))
|
Ok((topic_name, indexed))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_event(abi: &str) -> Result<Event, String> {
|
|
||||||
let abi = if !abi.trim_start().starts_with("event ") {
|
|
||||||
format!("event {}", abi)
|
|
||||||
} else {
|
|
||||||
abi.to_string()
|
|
||||||
};
|
|
||||||
AbiParser::default()
|
|
||||||
.parse_event(&abi)
|
|
||||||
.map_err(|err| format!("Failed to parse the event ABI: {:?}", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All the attributes the `EthEvent` macro supports
|
/// All the attributes the `EthEvent` macro supports
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct EthEventAttributes {
|
struct EthEventAttributes {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ethabi::{Event, EventParam, ParamType};
|
use ethabi::{Event, EventParam, Function, Param, ParamType, StateMutability};
|
||||||
use std::{fmt, iter::Peekable, str::CharIndices};
|
use std::{fmt, iter::Peekable, str::CharIndices};
|
||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ impl<'input> Token<'input> {
|
||||||
Token::Bool => ParamType::Bool,
|
Token::Bool => ParamType::Bool,
|
||||||
Token::Address => ParamType::Address,
|
Token::Address => ParamType::Address,
|
||||||
Token::String => ParamType::String,
|
Token::String => ParamType::String,
|
||||||
|
Token::Tuple => ParamType::Tuple(vec![]),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -307,6 +308,18 @@ impl<'input> HumanReadableParser<'input> {
|
||||||
Self::new(input).take_param()
|
Self::new(input).take_param()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a [Function] from a human readable form
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ethers_core::abi::HumanReadableParser;
|
||||||
|
/// let mut fun = HumanReadableParser::parse_function("function get(address author, string oldValue, string newValue)").unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn parse_function(input: &'input str) -> Result<Function, LexerError> {
|
||||||
|
Self::new(input).take_function()
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses an [Event] from a human readable form
|
/// Parses an [Event] from a human readable form
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -319,10 +332,58 @@ impl<'input> HumanReadableParser<'input> {
|
||||||
Self::new(input).take_event()
|
Self::new(input).take_event()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn take_function(&mut self) -> Result<Function, LexerError> {
|
||||||
|
let name = self.take_identifier(Token::Function)?;
|
||||||
|
|
||||||
|
self.take_open_parenthesis()?;
|
||||||
|
let inputs = self.take_function_params()?;
|
||||||
|
self.take_close_parenthesis()?;
|
||||||
|
|
||||||
|
let mut state_mutability = Default::default();
|
||||||
|
let mut outputs = vec![];
|
||||||
|
if self.peek().is_some() {
|
||||||
|
let _visibility = self.take_visibility();
|
||||||
|
if let Some(mutability) = self.take_state_mutability() {
|
||||||
|
state_mutability = mutability;
|
||||||
|
}
|
||||||
|
if self.peek_next(Token::Virtual) {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
if self.peek_next(Token::Override) {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
if self.peek_next(Token::Returns) {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.peek_next(Token::OpenParenthesis) {
|
||||||
|
self.take_open_parenthesis()?;
|
||||||
|
outputs = self.take_function_params()?;
|
||||||
|
self.take_close_parenthesis()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
#[allow(deprecated)]
|
||||||
|
Function { name: name.to_string(), inputs, outputs, constant: None, state_mutability },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn take_event(&mut self) -> Result<Event, LexerError> {
|
pub fn take_event(&mut self) -> Result<Event, LexerError> {
|
||||||
|
let name = self.take_identifier(Token::Event)?;
|
||||||
|
self.take_open_parenthesis()?;
|
||||||
|
let inputs = self.take_event_params()?;
|
||||||
|
self.take_close_parenthesis()?;
|
||||||
|
let event = Event { name: name.to_string(), inputs, anonymous: self.take_anonymous() };
|
||||||
|
|
||||||
|
Ok(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an identifier, optionally prefixed with a token like `function? <name>`
|
||||||
|
fn take_identifier(&mut self, prefixed: Token) -> Result<&'input str, LexerError> {
|
||||||
let (l, token, r) = self.next_spanned()?;
|
let (l, token, r) = self.next_spanned()?;
|
||||||
let name = match token {
|
let name = match token {
|
||||||
Token::Event => {
|
i if i == prefixed => {
|
||||||
let (_, next, _) = self.lexer.peek().cloned().ok_or(LexerError::EndOfFile)??;
|
let (_, next, _) = self.lexer.peek().cloned().ok_or(LexerError::EndOfFile)??;
|
||||||
if let Token::Identifier(name) = next {
|
if let Token::Identifier(name) = next {
|
||||||
self.next();
|
self.next();
|
||||||
|
@ -334,13 +395,74 @@ impl<'input> HumanReadableParser<'input> {
|
||||||
Token::Identifier(name) => name,
|
Token::Identifier(name) => name,
|
||||||
t => unrecognised!(l, r, t.to_string()),
|
t => unrecognised!(l, r, t.to_string()),
|
||||||
};
|
};
|
||||||
|
Ok(name)
|
||||||
|
}
|
||||||
|
|
||||||
self.take_open_parenthesis()?;
|
fn take_name_opt(&mut self) -> Result<Option<&'input str>, LexerError> {
|
||||||
let inputs = self.take_event_params()?;
|
if let (_, Token::Identifier(name), _) = self.peek_some()? {
|
||||||
self.take_close_parenthesis()?;
|
self.next();
|
||||||
let event = Event { name: name.to_string(), inputs, anonymous: self.take_anonymous() };
|
Ok(Some(name))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(event)
|
fn take_visibility(&mut self) -> Option<Visibility> {
|
||||||
|
match self.lexer.peek() {
|
||||||
|
Some(Ok((_, Token::Internal, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(Visibility::Internal)
|
||||||
|
}
|
||||||
|
Some(Ok((_, Token::External, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(Visibility::External)
|
||||||
|
}
|
||||||
|
Some(Ok((_, Token::Private, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(Visibility::Private)
|
||||||
|
}
|
||||||
|
Some(Ok((_, Token::Public, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(Visibility::Public)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_state_mutability(&mut self) -> Option<StateMutability> {
|
||||||
|
match self.lexer.peek() {
|
||||||
|
Some(Ok((_, Token::View, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(StateMutability::View)
|
||||||
|
}
|
||||||
|
Some(Ok((_, Token::Pure, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(StateMutability::Pure)
|
||||||
|
}
|
||||||
|
Some(Ok((_, Token::Payable, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(StateMutability::Payable)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_data_location(&mut self) -> Option<DataLocation> {
|
||||||
|
match self.lexer.peek() {
|
||||||
|
Some(Ok((_, Token::Memory, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(DataLocation::Memory)
|
||||||
|
}
|
||||||
|
Some(Ok((_, Token::Storage, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(DataLocation::Storage)
|
||||||
|
}
|
||||||
|
Some(Ok((_, Token::Calldata, _))) => {
|
||||||
|
self.next();
|
||||||
|
Some(DataLocation::Calldata)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_anonymous(&mut self) -> bool {
|
fn take_anonymous(&mut self) -> bool {
|
||||||
|
@ -352,16 +474,19 @@ impl<'input> HumanReadableParser<'input> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses all event params
|
/// Takes comma separated values via `f` until the `token` is parsed
|
||||||
fn take_event_params(&mut self) -> Result<Vec<EventParam>, LexerError> {
|
fn take_csv_until<T, F>(&mut self, token: Token, f: F) -> Result<Vec<T>, LexerError>
|
||||||
|
where
|
||||||
|
F: Fn(&mut Self) -> Result<T, LexerError>,
|
||||||
|
{
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
if self.peek_next(Token::CloseParenthesis) {
|
if self.peek_next(token) {
|
||||||
return Ok(params)
|
return Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
params.push(self.take_event_param()?);
|
params.push(f(self)?);
|
||||||
|
|
||||||
let (l, next, r) = match self.peek() {
|
let (l, next, r) = match self.peek() {
|
||||||
Some(next) => next?,
|
Some(next) => next?,
|
||||||
|
@ -369,16 +494,33 @@ impl<'input> HumanReadableParser<'input> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match next {
|
match next {
|
||||||
|
i if i == token => break,
|
||||||
Token::Comma => {
|
Token::Comma => {
|
||||||
self.next_spanned()?;
|
self.next_spanned()?;
|
||||||
}
|
}
|
||||||
Token::CloseParenthesis => break,
|
|
||||||
t => unrecognised!(l, r, t.to_string()),
|
t => unrecognised!(l, r, t.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses all function input params
|
||||||
|
fn take_function_params(&mut self) -> Result<Vec<Param>, LexerError> {
|
||||||
|
self.take_csv_until(Token::CloseParenthesis, |s| s.take_input_param())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_input_param(&mut self) -> Result<Param, LexerError> {
|
||||||
|
let kind = self.take_param()?;
|
||||||
|
let _location = self.take_data_location();
|
||||||
|
let name = self.take_name_opt()?.unwrap_or("");
|
||||||
|
Ok(Param { name: name.to_string(), kind, internal_type: None })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses all event params
|
||||||
|
fn take_event_params(&mut self) -> Result<Vec<EventParam>, LexerError> {
|
||||||
|
self.take_csv_until(Token::CloseParenthesis, |s| s.take_event_param())
|
||||||
|
}
|
||||||
|
|
||||||
fn take_event_param(&mut self) -> Result<EventParam, LexerError> {
|
fn take_event_param(&mut self) -> Result<EventParam, LexerError> {
|
||||||
let kind = self.take_param()?;
|
let kind = self.take_param()?;
|
||||||
let mut name = "";
|
let mut name = "";
|
||||||
|
@ -651,10 +793,146 @@ fn keyword(id: &str) -> Option<Token> {
|
||||||
Some(token)
|
Some(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub enum Visibility {
|
||||||
|
Internal,
|
||||||
|
External,
|
||||||
|
Private,
|
||||||
|
Public,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub enum DataLocation {
|
||||||
|
Memory,
|
||||||
|
Storage,
|
||||||
|
Calldata,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_function() {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let f = Function {
|
||||||
|
name: "get".to_string(),
|
||||||
|
inputs: vec![
|
||||||
|
Param { name: "author".to_string(), kind: ParamType::Address, internal_type: None },
|
||||||
|
Param {
|
||||||
|
name: "oldValue".to_string(),
|
||||||
|
kind: ParamType::String,
|
||||||
|
internal_type: None,
|
||||||
|
},
|
||||||
|
Param {
|
||||||
|
name: "newValue".to_string(),
|
||||||
|
kind: ParamType::String,
|
||||||
|
internal_type: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
outputs: vec![],
|
||||||
|
constant: None,
|
||||||
|
state_mutability: Default::default(),
|
||||||
|
};
|
||||||
|
let parsed = HumanReadableParser::parse_function(
|
||||||
|
"function get(address author, string oldValue, string newValue)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(f, parsed);
|
||||||
|
|
||||||
|
let parsed = HumanReadableParser::parse_function(
|
||||||
|
"get(address author, string oldValue, string newValue)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(f, parsed);
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let f = Function {
|
||||||
|
name: "get".to_string(),
|
||||||
|
inputs: vec![
|
||||||
|
Param { name: "".to_string(), kind: ParamType::Address, internal_type: None },
|
||||||
|
Param { name: "".to_string(), kind: ParamType::String, internal_type: None },
|
||||||
|
Param { name: "".to_string(), kind: ParamType::String, internal_type: None },
|
||||||
|
],
|
||||||
|
outputs: vec![],
|
||||||
|
constant: None,
|
||||||
|
state_mutability: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed =
|
||||||
|
HumanReadableParser::parse_function("get(address , string , string )").unwrap();
|
||||||
|
assert_eq!(f, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_function_output() {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let f = Function {
|
||||||
|
name: "get".to_string(),
|
||||||
|
inputs: vec![
|
||||||
|
Param { name: "author".to_string(), kind: ParamType::Address, internal_type: None },
|
||||||
|
Param {
|
||||||
|
name: "oldValue".to_string(),
|
||||||
|
kind: ParamType::String,
|
||||||
|
internal_type: None,
|
||||||
|
},
|
||||||
|
Param {
|
||||||
|
name: "newValue".to_string(),
|
||||||
|
kind: ParamType::String,
|
||||||
|
internal_type: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
outputs: vec![
|
||||||
|
Param {
|
||||||
|
name: "result".to_string(),
|
||||||
|
kind: ParamType::Uint(256),
|
||||||
|
internal_type: None,
|
||||||
|
},
|
||||||
|
Param { name: "output".to_string(), kind: ParamType::Address, internal_type: None },
|
||||||
|
],
|
||||||
|
constant: None,
|
||||||
|
state_mutability: Default::default(),
|
||||||
|
};
|
||||||
|
let parsed = HumanReadableParser::parse_function(
|
||||||
|
"function get(address author, string oldValue, string newValue) returns (uint256 result, address output)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(f, parsed);
|
||||||
|
|
||||||
|
let parsed = HumanReadableParser::parse_function(
|
||||||
|
" get(address author, string oldValue, string newValue) returns (uint256 result, address output)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(f, parsed);
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let mut f = Function {
|
||||||
|
name: "get".to_string(),
|
||||||
|
inputs: vec![
|
||||||
|
Param { name: "".to_string(), kind: ParamType::Address, internal_type: None },
|
||||||
|
Param { name: "".to_string(), kind: ParamType::String, internal_type: None },
|
||||||
|
Param { name: "".to_string(), kind: ParamType::String, internal_type: None },
|
||||||
|
],
|
||||||
|
outputs: vec![
|
||||||
|
Param { name: "".to_string(), kind: ParamType::Uint(256), internal_type: None },
|
||||||
|
Param { name: "".to_string(), kind: ParamType::Address, internal_type: None },
|
||||||
|
],
|
||||||
|
constant: None,
|
||||||
|
state_mutability: Default::default(),
|
||||||
|
};
|
||||||
|
let parsed = HumanReadableParser::parse_function(
|
||||||
|
"function get(address, string, string) (uint256, address)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(f, parsed);
|
||||||
|
|
||||||
|
f.state_mutability = StateMutability::View;
|
||||||
|
let parsed = HumanReadableParser::parse_function(
|
||||||
|
"function get(address, string memory, string calldata) public view (uint256, address)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(f, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_param() {
|
fn test_parse_param() {
|
||||||
assert_eq!(HumanReadableParser::parse_type("address").unwrap(), ParamType::Address);
|
assert_eq!(HumanReadableParser::parse_type("address").unwrap(), ParamType::Address);
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::collections::{BTreeMap, HashMap, VecDeque};
|
||||||
|
|
||||||
use crate::abi::{
|
use crate::abi::{
|
||||||
error::{bail, format_err, ParseError, Result},
|
error::{bail, format_err, ParseError, Result},
|
||||||
param_type::Reader,
|
|
||||||
struct_def::{FieldType, StructFieldType},
|
struct_def::{FieldType, StructFieldType},
|
||||||
Abi, Constructor, Event, EventParam, Function, Param, ParamType, SolStruct, StateMutability,
|
Abi, Constructor, Event, EventParam, Function, HumanReadableParser, Param, ParamType,
|
||||||
|
SolStruct, StateMutability,
|
||||||
};
|
};
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
|
|
||||||
|
@ -372,14 +372,8 @@ impl AbiParser {
|
||||||
/// contains a `uint8`. This however can still lead to false detection of `uint8` and is only
|
/// contains a `uint8`. This however can still lead to false detection of `uint8` and is only
|
||||||
/// solvable with a more sophisticated parser: <https://github.com/gakonst/ethers-rs/issues/474>
|
/// solvable with a more sophisticated parser: <https://github.com/gakonst/ethers-rs/issues/474>
|
||||||
fn parse_type(&self, type_str: &str) -> Result<(ParamType, Option<String>)> {
|
fn parse_type(&self, type_str: &str) -> Result<(ParamType, Option<String>)> {
|
||||||
if let Ok(kind) = Reader::read(type_str) {
|
if let Ok(kind) = HumanReadableParser::parse_type(type_str) {
|
||||||
if is_likely_tuple_not_uint8(&kind, type_str) {
|
|
||||||
// if we detected an `ParamType::Uint(8)` but the input string does not include a
|
|
||||||
// `uint8` then it's highly likely that we try parsing a struct instead
|
|
||||||
self.parse_struct_type(type_str)
|
|
||||||
} else {
|
|
||||||
Ok((kind, None))
|
Ok((kind, None))
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// try struct instead
|
// try struct instead
|
||||||
self.parse_struct_type(type_str)
|
self.parse_struct_type(type_str)
|
||||||
|
@ -516,30 +510,6 @@ fn detect_state_mutability(s: &str) -> StateMutability {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the input `ParamType` contains a `uint8` that the `type_str` also contains `uint8`
|
|
||||||
///
|
|
||||||
/// Returns `true` if `kind` contains `uint8` but the type_str doesnt
|
|
||||||
///
|
|
||||||
/// See `AbiParser::parse_type`
|
|
||||||
pub(crate) fn is_likely_tuple_not_uint8(kind: &ParamType, type_str: &str) -> bool {
|
|
||||||
if contains_uint8(kind) {
|
|
||||||
!type_str.contains("uint8")
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the `ParamType` contains an `uint8`
|
|
||||||
pub fn contains_uint8(kind: &ParamType) -> bool {
|
|
||||||
match kind {
|
|
||||||
ParamType::Uint(8) => true,
|
|
||||||
ParamType::Array(kind) => contains_uint8(kind),
|
|
||||||
ParamType::FixedArray(kind, _) => contains_uint8(kind),
|
|
||||||
ParamType::Tuple(tuple) => tuple.iter().any(contains_uint8),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_first_ident_char(c: char) -> bool {
|
pub(crate) fn is_first_ident_char(c: char) -> bool {
|
||||||
matches!(c, 'a'..='z' | 'A'..='Z' | '_')
|
matches!(c, 'a'..='z' | 'A'..='Z' | '_')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
//! Solidity struct definition parsing support
|
//! Solidity struct definition parsing support
|
||||||
use crate::abi::{
|
use crate::abi::{
|
||||||
error::{bail, format_err, Result},
|
error::{bail, format_err, Result},
|
||||||
human_readable::{is_likely_tuple_not_uint8, is_whitespace, parse_identifier},
|
human_readable::{is_whitespace, parse_identifier},
|
||||||
param_type::Reader,
|
HumanReadableParser, ParamType,
|
||||||
ParamType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A field declaration inside a struct
|
/// A field declaration inside a struct
|
||||||
|
@ -337,14 +336,8 @@ fn parse_field_type(s: &str) -> Result<FieldType> {
|
||||||
// special case for `address payable`
|
// special case for `address payable`
|
||||||
input = input[..input.len() - 7].trim_end();
|
input = input[..input.len() - 7].trim_end();
|
||||||
}
|
}
|
||||||
if let Ok(ty) = Reader::read(input) {
|
if let Ok(ty) = HumanReadableParser::parse_type(input) {
|
||||||
// See `AbiParser::parse_type`
|
|
||||||
if is_likely_tuple_not_uint8(&ty, s) {
|
|
||||||
// likely that an unknown type was resolved as `uint8`
|
|
||||||
StructFieldType::parse(input.trim_end())
|
|
||||||
} else {
|
|
||||||
Ok(FieldType::Elementary(ty))
|
Ok(FieldType::Elementary(ty))
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// parsing elementary datatype failed, try struct
|
// parsing elementary datatype failed, try struct
|
||||||
StructFieldType::parse(input.trim_end())
|
StructFieldType::parse(input.trim_end())
|
||||||
|
@ -363,16 +356,12 @@ fn parse_mapping(s: &str) -> Result<MappingType> {
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| format_err!("Expected mapping key type at `{}`", input))
|
.ok_or_else(|| format_err!("Expected mapping key type at `{}`", input))
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.map(Reader::read)??;
|
.map(HumanReadableParser::parse_type)??;
|
||||||
|
|
||||||
let is_illegal_ty = if let ParamType::Array(_) |
|
let is_illegal_ty = matches!(
|
||||||
ParamType::FixedArray(_, _) |
|
&key_type,
|
||||||
ParamType::Tuple(_) = &key_type
|
ParamType::Array(_) | ParamType::FixedArray(_, _) | ParamType::Tuple(_)
|
||||||
{
|
);
|
||||||
true
|
|
||||||
} else {
|
|
||||||
is_likely_tuple_not_uint8(&key_type, s)
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_illegal_ty {
|
if is_illegal_ty {
|
||||||
bail!("Expected elementary mapping key type at `{}` got {:?}", input, key_type)
|
bail!("Expected elementary mapping key type at `{}` got {:?}", input, key_type)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Taken from <https://github.com/tomusdrw/rust-web3/blob/master/src/types/block.rs>
|
// Taken from <https://github.com/tomusdrw/rust-web3/blob/master/src/types/block.rs>
|
||||||
use crate::types::{Address, Bloom, Bytes, Transaction, TxHash, H256, H64, U256, U64};
|
use crate::types::{Address, Bloom, Bytes, Transaction, TxHash, H256, U256, U64};
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
#[cfg(not(feature = "celo"))]
|
#[cfg(not(feature = "celo"))]
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
|
@ -15,7 +15,7 @@ use thiserror::Error;
|
||||||
/// The block type returned from RPC calls.
|
/// The block type returned from RPC calls.
|
||||||
/// This is generic over a `TX` type which will be either the hash or the full transaction,
|
/// This is generic over a `TX` type which will be either the hash or the full transaction,
|
||||||
/// i.e. `Block<TxHash>` or Block<Transaction>`.
|
/// i.e. `Block<TxHash>` or Block<Transaction>`.
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
pub struct Block<TX> {
|
pub struct Block<TX> {
|
||||||
/// Hash of the block
|
/// Hash of the block
|
||||||
pub hash: Option<H256>,
|
pub hash: Option<H256>,
|
||||||
|
@ -81,7 +81,7 @@ pub struct Block<TX> {
|
||||||
pub mix_hash: Option<H256>,
|
pub mix_hash: Option<H256>,
|
||||||
/// Nonce
|
/// Nonce
|
||||||
#[cfg(not(feature = "celo"))]
|
#[cfg(not(feature = "celo"))]
|
||||||
pub nonce: Option<H64>,
|
pub nonce: Option<crate::types::H64>,
|
||||||
/// Base fee per unit of gas (if past London)
|
/// Base fee per unit of gas (if past London)
|
||||||
#[serde(rename = "baseFeePerGas")]
|
#[serde(rename = "baseFeePerGas")]
|
||||||
pub base_fee_per_gas: Option<U256>,
|
pub base_fee_per_gas: Option<U256>,
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub struct BlockTrace {
|
||||||
|
|
||||||
//---------------- State Diff ----------------
|
//---------------- State Diff ----------------
|
||||||
/// Aux type for Diff::Changed.
|
/// Aux type for Diff::Changed.
|
||||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||||
pub struct ChangedType<T> {
|
pub struct ChangedType<T> {
|
||||||
/// Previous value.
|
/// Previous value.
|
||||||
pub from: T,
|
pub from: T,
|
||||||
|
@ -51,7 +51,7 @@ pub struct ChangedType<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serde-friendly `Diff` shadow.
|
/// Serde-friendly `Diff` shadow.
|
||||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||||
pub enum Diff<T> {
|
pub enum Diff<T> {
|
||||||
/// No change.
|
/// No change.
|
||||||
#[serde(rename = "=")]
|
#[serde(rename = "=")]
|
||||||
|
@ -68,7 +68,7 @@ pub enum Diff<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serde-friendly `AccountDiff` shadow.
|
/// Serde-friendly `AccountDiff` shadow.
|
||||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||||
pub struct AccountDiff {
|
pub struct AccountDiff {
|
||||||
/// Account balance.
|
/// Account balance.
|
||||||
pub balance: Diff<U256>,
|
pub balance: Diff<U256>,
|
||||||
|
@ -81,7 +81,7 @@ pub struct AccountDiff {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serde-friendly `StateDiff` shadow.
|
/// Serde-friendly `StateDiff` shadow.
|
||||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||||
pub struct StateDiff(pub BTreeMap<H160, AccountDiff>);
|
pub struct StateDiff(pub BTreeMap<H160, AccountDiff>);
|
||||||
|
|
||||||
// ------------------ Trace -------------
|
// ------------------ Trace -------------
|
||||||
|
|
Loading…
Reference in New Issue