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
|
||||
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::{
|
||||
contract::{types, Context},
|
||||
rawabi::{Component, RawAbi},
|
||||
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 {
|
||||
/// 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
|
||||
.components
|
||||
.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<_>>>()
|
||||
{
|
||||
let s = SolStruct { name: ident.to_string(), fields };
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
//! 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 quote::quote;
|
||||
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
|
||||
pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
|
||||
let attributes = match parse_call_attributes(&input) {
|
||||
|
@ -22,49 +20,21 @@ pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
|
|||
attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
|
||||
|
||||
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
|
||||
if let Ok(fun) = parse_function(&src) {
|
||||
if let Ok(fun) = HumanReadableParser::parse_function(&src) {
|
||||
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 {
|
||||
// try to determine the abi by using its fields at runtime
|
||||
return match derive_trait_impls_with_abi_type(
|
||||
&input,
|
||||
&function_call_name,
|
||||
Some(raw_function_signature),
|
||||
) {
|
||||
Ok(derived) => derived,
|
||||
Err(err) => {
|
||||
Error::new(span, format!("Unable to determine ABI for `{}` : {}", src, err))
|
||||
.to_compile_error()
|
||||
}
|
||||
// try to determine the abi by using its fields at runtime
|
||||
return match derive_trait_impls_with_abi_type(
|
||||
&input,
|
||||
&function_call_name,
|
||||
Some(raw_function_sig),
|
||||
) {
|
||||
Ok(derived) => derived,
|
||||
Err(err) => {
|
||||
Error::new(span, format!("Unable to determine ABI for `{}` : {}", src, err))
|
||||
.to_compile_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -287,15 +257,3 @@ fn parse_call_attributes(input: &DeriveInput) -> Result<EthCallAttributes, Token
|
|||
}
|
||||
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::{
|
||||
abi::{param_type::Reader, AbiParser, Event, EventExt, EventParam, ParamType},
|
||||
abi::{Event, EventExt, EventParam, HumanReadableParser},
|
||||
macros::{ethers_contract_crate, ethers_core_crate},
|
||||
};
|
||||
use hex::FromHex;
|
||||
|
@ -32,41 +32,24 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
|
|||
|
||||
let mut event = if let Some((src, span)) = attributes.abi {
|
||||
// try to parse as solidity event
|
||||
if let Ok(event) = parse_event(&src) {
|
||||
if let Ok(event) = HumanReadableParser::parse_event(&src) {
|
||||
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 {
|
||||
match src.parse::<Source>().and_then(|s| s.get()) {
|
||||
Ok(abi) => {
|
||||
// try to derive the signature from the abi from the parsed abi
|
||||
// TODO(mattsse): this will fail for events that contain other non
|
||||
// elementary types in their abi because the parser
|
||||
// doesn't know how to substitute the types
|
||||
// this could be mitigated by getting the ABI of each non elementary type
|
||||
// at runtime and computing the the signature as
|
||||
// `static Lazy::...`
|
||||
match parse_event(&abi) {
|
||||
Ok(event) => event,
|
||||
Err(err) => return Error::new(span, err).to_compile_error(),
|
||||
}
|
||||
match src.parse::<Source>().and_then(|s| s.get()) {
|
||||
Ok(abi) => {
|
||||
// try to derive the signature from the abi from the parsed abi
|
||||
// TODO(mattsse): this will fail for events that contain other non
|
||||
// elementary types in their abi because the parser
|
||||
// doesn't know how to substitute the types
|
||||
// this could be mitigated by getting the ABI of each non elementary type
|
||||
// at runtime and computing the the signature as
|
||||
// `static Lazy::...`
|
||||
match HumanReadableParser::parse_event(&abi) {
|
||||
Ok(event) => event,
|
||||
Err(err) => return Error::new(span, err).to_compile_error(),
|
||||
}
|
||||
Err(err) => return Error::new(span, err).to_compile_error(),
|
||||
}
|
||||
Err(err) => return Error::new(span, err).to_compile_error(),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -362,17 +345,6 @@ fn parse_field_attributes(field: &Field) -> Result<(Option<String>, bool), Error
|
|||
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
|
||||
#[derive(Default)]
|
||||
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 unicode_xid::UnicodeXID;
|
||||
|
||||
|
@ -81,6 +81,7 @@ impl<'input> Token<'input> {
|
|||
Token::Bool => ParamType::Bool,
|
||||
Token::Address => ParamType::Address,
|
||||
Token::String => ParamType::String,
|
||||
Token::Tuple => ParamType::Tuple(vec![]),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
@ -307,6 +308,18 @@ impl<'input> HumanReadableParser<'input> {
|
|||
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
|
||||
///
|
||||
/// # Example
|
||||
|
@ -319,10 +332,58 @@ impl<'input> HumanReadableParser<'input> {
|
|||
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> {
|
||||
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 name = match token {
|
||||
Token::Event => {
|
||||
i if i == prefixed => {
|
||||
let (_, next, _) = self.lexer.peek().cloned().ok_or(LexerError::EndOfFile)??;
|
||||
if let Token::Identifier(name) = next {
|
||||
self.next();
|
||||
|
@ -334,13 +395,74 @@ impl<'input> HumanReadableParser<'input> {
|
|||
Token::Identifier(name) => name,
|
||||
t => unrecognised!(l, r, t.to_string()),
|
||||
};
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
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() };
|
||||
fn take_name_opt(&mut self) -> Result<Option<&'input str>, LexerError> {
|
||||
if let (_, Token::Identifier(name), _) = self.peek_some()? {
|
||||
self.next();
|
||||
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 {
|
||||
|
@ -352,16 +474,19 @@ impl<'input> HumanReadableParser<'input> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parses all event params
|
||||
fn take_event_params(&mut self) -> Result<Vec<EventParam>, LexerError> {
|
||||
/// Takes comma separated values via `f` until the `token` is parsed
|
||||
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();
|
||||
|
||||
if self.peek_next(Token::CloseParenthesis) {
|
||||
if self.peek_next(token) {
|
||||
return Ok(params)
|
||||
}
|
||||
|
||||
loop {
|
||||
params.push(self.take_event_param()?);
|
||||
params.push(f(self)?);
|
||||
|
||||
let (l, next, r) = match self.peek() {
|
||||
Some(next) => next?,
|
||||
|
@ -369,16 +494,33 @@ impl<'input> HumanReadableParser<'input> {
|
|||
};
|
||||
|
||||
match next {
|
||||
i if i == token => break,
|
||||
Token::Comma => {
|
||||
self.next_spanned()?;
|
||||
}
|
||||
Token::CloseParenthesis => break,
|
||||
t => unrecognised!(l, r, t.to_string()),
|
||||
}
|
||||
}
|
||||
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> {
|
||||
let kind = self.take_param()?;
|
||||
let mut name = "";
|
||||
|
@ -651,10 +793,146 @@ fn keyword(id: &str) -> Option<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)]
|
||||
mod tests {
|
||||
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]
|
||||
fn test_parse_param() {
|
||||
assert_eq!(HumanReadableParser::parse_type("address").unwrap(), ParamType::Address);
|
||||
|
|
|
@ -2,9 +2,9 @@ use std::collections::{BTreeMap, HashMap, VecDeque};
|
|||
|
||||
use crate::abi::{
|
||||
error::{bail, format_err, ParseError, Result},
|
||||
param_type::Reader,
|
||||
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;
|
||||
|
||||
|
@ -372,14 +372,8 @@ impl AbiParser {
|
|||
/// 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>
|
||||
fn parse_type(&self, type_str: &str) -> Result<(ParamType, Option<String>)> {
|
||||
if let Ok(kind) = Reader::read(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))
|
||||
}
|
||||
if let Ok(kind) = HumanReadableParser::parse_type(type_str) {
|
||||
Ok((kind, None))
|
||||
} else {
|
||||
// try struct instead
|
||||
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 {
|
||||
matches!(c, 'a'..='z' | 'A'..='Z' | '_')
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
//! Solidity struct definition parsing support
|
||||
use crate::abi::{
|
||||
error::{bail, format_err, Result},
|
||||
human_readable::{is_likely_tuple_not_uint8, is_whitespace, parse_identifier},
|
||||
param_type::Reader,
|
||||
ParamType,
|
||||
human_readable::{is_whitespace, parse_identifier},
|
||||
HumanReadableParser, ParamType,
|
||||
};
|
||||
|
||||
/// A field declaration inside a struct
|
||||
|
@ -337,14 +336,8 @@ fn parse_field_type(s: &str) -> Result<FieldType> {
|
|||
// special case for `address payable`
|
||||
input = input[..input.len() - 7].trim_end();
|
||||
}
|
||||
if let Ok(ty) = Reader::read(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))
|
||||
}
|
||||
if let Ok(ty) = HumanReadableParser::parse_type(input) {
|
||||
Ok(FieldType::Elementary(ty))
|
||||
} else {
|
||||
// parsing elementary datatype failed, try struct
|
||||
StructFieldType::parse(input.trim_end())
|
||||
|
@ -363,16 +356,12 @@ fn parse_mapping(s: &str) -> Result<MappingType> {
|
|||
.next()
|
||||
.ok_or_else(|| format_err!("Expected mapping key type at `{}`", input))
|
||||
.map(str::trim)
|
||||
.map(Reader::read)??;
|
||||
.map(HumanReadableParser::parse_type)??;
|
||||
|
||||
let is_illegal_ty = if let ParamType::Array(_) |
|
||||
ParamType::FixedArray(_, _) |
|
||||
ParamType::Tuple(_) = &key_type
|
||||
{
|
||||
true
|
||||
} else {
|
||||
is_likely_tuple_not_uint8(&key_type, s)
|
||||
};
|
||||
let is_illegal_ty = matches!(
|
||||
&key_type,
|
||||
ParamType::Array(_) | ParamType::FixedArray(_, _) | ParamType::Tuple(_)
|
||||
);
|
||||
|
||||
if is_illegal_ty {
|
||||
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>
|
||||
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};
|
||||
#[cfg(not(feature = "celo"))]
|
||||
use core::cmp::Ordering;
|
||||
|
@ -15,7 +15,7 @@ use thiserror::Error;
|
|||
/// The block type returned from RPC calls.
|
||||
/// This is generic over a `TX` type which will be either the hash or the full 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> {
|
||||
/// Hash of the block
|
||||
pub hash: Option<H256>,
|
||||
|
@ -81,7 +81,7 @@ pub struct Block<TX> {
|
|||
pub mix_hash: Option<H256>,
|
||||
/// Nonce
|
||||
#[cfg(not(feature = "celo"))]
|
||||
pub nonce: Option<H64>,
|
||||
pub nonce: Option<crate::types::H64>,
|
||||
/// Base fee per unit of gas (if past London)
|
||||
#[serde(rename = "baseFeePerGas")]
|
||||
pub base_fee_per_gas: Option<U256>,
|
||||
|
|
|
@ -42,7 +42,7 @@ pub struct BlockTrace {
|
|||
|
||||
//---------------- State Diff ----------------
|
||||
/// Aux type for Diff::Changed.
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
pub struct ChangedType<T> {
|
||||
/// Previous value.
|
||||
pub from: T,
|
||||
|
@ -51,7 +51,7 @@ pub struct ChangedType<T> {
|
|||
}
|
||||
|
||||
/// Serde-friendly `Diff` shadow.
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
pub enum Diff<T> {
|
||||
/// No change.
|
||||
#[serde(rename = "=")]
|
||||
|
@ -68,7 +68,7 @@ pub enum Diff<T> {
|
|||
}
|
||||
|
||||
/// Serde-friendly `AccountDiff` shadow.
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
pub struct AccountDiff {
|
||||
/// Account balance.
|
||||
pub balance: Diff<U256>,
|
||||
|
@ -81,7 +81,7 @@ pub struct AccountDiff {
|
|||
}
|
||||
|
||||
/// Serde-friendly `StateDiff` shadow.
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
pub struct StateDiff(pub BTreeMap<H160, AccountDiff>);
|
||||
|
||||
// ------------------ Trace -------------
|
||||
|
|
Loading…
Reference in New Issue