From 9c0e8d2393345933922acf8d453e3cd6ccc4c231 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 11 Mar 2021 17:30:30 +0100 Subject: [PATCH 1/8] refactor: event parser --- ethers-core/src/abi/human_readable.rs | 228 +++++++++++++++++++++++--- 1 file changed, 209 insertions(+), 19 deletions(-) diff --git a/ethers-core/src/abi/human_readable.rs b/ethers-core/src/abi/human_readable.rs index e4df0881f..703961b06 100644 --- a/ethers-core/src/abi/human_readable.rs +++ b/ethers-core/src/abi/human_readable.rs @@ -1,9 +1,11 @@ -use super::{ - param_type::Reader, Abi, Event, EventParam, Function, Param, ParamType, StateMutability, -}; use std::collections::HashMap; + use thiserror::Error; +use super::{ + Abi, Event, EventParam, Function, Param, param_type::Reader, ParamType, StateMutability, +}; + /// Parses a "human readable abi" string vector /// /// ``` @@ -45,6 +47,154 @@ pub fn parse(input: &[&str]) -> Result { Ok(abi) } +/// Parses a solidity event declaration from `event (args*) anonymous?` +fn parse_event2(mut event: &str) -> Result { + if !event.starts_with("event ") { + return Err(ParseError::ParseError(super::Error::InvalidData)); + } + event = &event[6..]; + let mut chars = event.chars(); + let mut name = String::new(); + + let c = chars.next().unwrap(); + if is_first_ident_char(c) { + name.push(c); + loop { + match chars.clone().next() { + Some(c) if is_ident_char(c) => { + chars.next(); + name.push(c); + } + _ => break, + } + } + } + if name.is_empty() { + return Err(ParseError::ParseError(super::Error::InvalidName(event.to_owned()))); + } + loop { + match chars.next() { + None => return Err(ParseError::ParseError(super::Error::InvalidData)), + Some('(') => { + event = chars.as_str().trim(); + let (inputs, anonymous) = parse_event_args(event)?; + return Ok(Event { + name, + anonymous, + inputs, + }) + } + Some('\\') => { + return match chars.next() { + Some('(') => { + event = chars.as_str().trim(); + let (inputs, anonymous) = parse_event_args(event)?; + Ok(Event { + name, + anonymous, + inputs, + }) + } + _ => Err(ParseError::ParseError(super::Error::InvalidData)), + } + } + Some(' ') | Some('\t') => { + continue; + } + _ => { + return Err(ParseError::ParseError(super::Error::InvalidData)); + } + } + } +} + +/// Returns the event parameters and whether the event was declared as anonymous +fn parse_event_args(mut input: &str) -> Result<(Vec, bool), ParseError> { + let mut anonymous = false; + if input.ends_with("anonymous") { + anonymous = true; + input = input[..input.len()-9].trim_end(); + } + + input = input.trim().strip_suffix(')').ok_or(ParseError::ParseError(super::Error::InvalidData))?; + input = input.trim_end_matches('\\'); + + let mut params = Vec::new(); + + if input.is_empty() { + return Ok((params, anonymous)) + } + + for arg in input.split(',') { + let mut iter = arg.trim().rsplitn(3, is_whitespace); + let name = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?; + let mid = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?; + let mut kind = None; + let mut indexed = false; + if mid == "indexed" { + indexed = true; + kind = iter.next().map(Reader::read); + } else { + kind = Some(Reader::read(mid)); + if iter.next().is_some() { + return Err(ParseError::ParseError(super::Error::InvalidData)); + } + } + params.push( + EventParam { + name: name.to_owned(), + kind: kind.ok_or(ParseError::ParseError(super::Error::InvalidData))??, + indexed, + } + ) + } + Ok((params, anonymous)) +} + + +fn parse2(input: &[&str]) -> Result { + let mut abi = Abi { + constructor: None, + functions: HashMap::new(), + events: HashMap::new(), + receive: false, + fallback: false, + }; + + for line in input { + let line = line.trim(); + let (prefix, mut remainder) = line.split_once(' ').ok_or(ParseError::ParseError(super::Error::InvalidData))?; + match prefix { + "event" => { + let event = parse_event2(&mut remainder)?; + abi.events + .entry(event.name.clone()) + .or_default() + .push(event); + } + "function" => {} + "constructor" => {} + _ => return Err(ParseError::ParseError(super::Error::InvalidData)) + } + } + + + Ok(abi) +} + + +fn is_first_ident_char(c: char) -> bool { + matches!(c, 'a'..='z' | 'A'..='Z' | '_') +} + +fn is_ident_char(c: char) -> bool { + matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_') +} + +fn is_whitespace(c: char) -> bool { + matches!(c, ' ' | '\t' ) +} + fn parse_event(event: &str) -> Result { let split: Vec<&str> = event.split("event ").collect(); let split: Vec<&str> = split[1].split('(').collect(); @@ -126,7 +276,7 @@ fn parse_function(fn_string: &str) -> Result { }; #[allow(deprecated)] - Ok(Function { + Ok(Function { name: fn_name.to_owned(), inputs, outputs, @@ -177,9 +327,9 @@ mod tests { let parsed = parse_function(fn_str).unwrap(); assert_eq!(parsed.name, "approve"); assert_eq!(parsed.inputs[0].name, "_spender"); - assert_eq!(parsed.inputs[0].kind, ParamType::Address,); + assert_eq!(parsed.inputs[0].kind, ParamType::Address, ); assert_eq!(parsed.inputs[1].name, "value"); - assert_eq!(parsed.inputs[1].kind, ParamType::Uint(256),); + assert_eq!(parsed.inputs[1].kind, ParamType::Uint(256), ); assert_eq!(parsed.outputs[0].name, ""); assert_eq!(parsed.outputs[0].kind, ParamType::Bool); } @@ -207,6 +357,34 @@ mod tests { assert!(parsed.outputs.is_empty()); } + #[test] + fn parses_event2() { + assert_eq!( + parse_event2(&mut "event Foo (address indexed x, uint y, bytes32[] z)").unwrap(), + Event { + anonymous: false, + name: "Foo".to_owned(), + inputs: vec![ + EventParam { + name: "x".to_owned(), + kind: ParamType::Address, + indexed: true, + }, + EventParam { + name: "y".to_owned(), + kind: ParamType::Uint(256), + indexed: false, + }, + EventParam { + name: "z".to_owned(), + kind: ParamType::Array(Box::new(ParamType::FixedBytes(32))), + indexed: false, + }, + ], + } + ); + } + #[test] fn parses_event() { assert_eq!( @@ -218,7 +396,7 @@ mod tests { EventParam { name: "x".to_owned(), kind: ParamType::Address, - indexed: true + indexed: true, }, EventParam { name: "y".to_owned(), @@ -230,7 +408,19 @@ mod tests { kind: ParamType::Array(Box::new(ParamType::FixedBytes(32))), indexed: false, }, - ] + ], + } + ); + } + + #[test] + fn parses_anonymous_event2() { + assert_eq!( + parse_event2(&mut "event Foo() anonymous").unwrap(), + Event { + anonymous: true, + name: "Foo".to_owned(), + inputs: vec![], } ); } @@ -254,7 +444,7 @@ mod tests { EventParam { name: "x".to_owned(), kind: ParamType::Address, - indexed: true + indexed: true, } ); @@ -263,7 +453,7 @@ mod tests { EventParam { name: "x".to_owned(), kind: ParamType::Address, - indexed: false + indexed: false, } ); } @@ -278,10 +468,10 @@ mod tests { "function bar(uint256[] memory x)", "function bar()", ] - .iter() - .for_each(|x| { - parse_function(x).unwrap(); - }); + .iter() + .for_each(|x| { + parse_function(x).unwrap(); + }); } #[test] @@ -294,10 +484,10 @@ mod tests { "bytes32[] memory", "bytes32[] memory z", ] - .iter() - .for_each(|x| { - parse_param(x).unwrap(); - }); + .iter() + .for_each(|x| { + parse_param(x).unwrap(); + }); } #[test] @@ -306,6 +496,6 @@ mod tests { "\"function setValue(string)\"", "\"function getValue() external view (string)\"", ]) - .unwrap(); + .unwrap(); } } From cbf046a07a64d861d5f920a39bed5d890a8bfe52 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 11 Mar 2021 20:47:20 +0100 Subject: [PATCH 2/8] refactor: function parser --- ethers-core/src/abi/human_readable.rs | 243 ++++++++++++++++++++------ 1 file changed, 193 insertions(+), 50 deletions(-) diff --git a/ethers-core/src/abi/human_readable.rs b/ethers-core/src/abi/human_readable.rs index 703961b06..7e5816c85 100644 --- a/ethers-core/src/abi/human_readable.rs +++ b/ethers-core/src/abi/human_readable.rs @@ -47,16 +47,11 @@ pub fn parse(input: &[&str]) -> Result { Ok(abi) } -/// Parses a solidity event declaration from `event (args*) anonymous?` -fn parse_event2(mut event: &str) -> Result { - if !event.starts_with("event ") { - return Err(ParseError::ParseError(super::Error::InvalidData)); - } - event = &event[6..]; - let mut chars = event.chars(); +/// Parses an identifier like event or function name +fn parse_identifier(input: &mut &str) -> Result { + let mut chars = input.trim_start().chars(); let mut name = String::new(); - - let c = chars.next().unwrap(); + let c = chars.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?; if is_first_ident_char(c) { name.push(c); loop { @@ -69,34 +64,41 @@ fn parse_event2(mut event: &str) -> Result { } } } + *input = chars.as_str(); + Ok(name) +} + +/// Parses a solidity event declaration from `event (args*) anonymous?` +fn parse_event2(mut event: &str) -> Result { + event = event.trim(); + if !event.starts_with("event ") { + return Err(ParseError::ParseError(super::Error::InvalidData)); + } + event = &event[6..]; + + let name = parse_identifier(&mut event)?; if name.is_empty() { return Err(ParseError::ParseError(super::Error::InvalidName(event.to_owned()))); } + + let mut chars = event.chars(); + loop { match chars.next() { None => return Err(ParseError::ParseError(super::Error::InvalidData)), Some('(') => { event = chars.as_str().trim(); - let (inputs, anonymous) = parse_event_args(event)?; + let mut anonymous = false; + if event.ends_with("anonymous") { + anonymous = true; + event = event[..event.len() - 9].trim_end(); + } + let inputs = parse_event_args(event)?; return Ok(Event { name, anonymous, inputs, - }) - } - Some('\\') => { - return match chars.next() { - Some('(') => { - event = chars.as_str().trim(); - let (inputs, anonymous) = parse_event_args(event)?; - Ok(Event { - name, - anonymous, - inputs, - }) - } - _ => Err(ParseError::ParseError(super::Error::InvalidData)), - } + }); } Some(' ') | Some('\t') => { continue; @@ -108,21 +110,14 @@ fn parse_event2(mut event: &str) -> Result { } } -/// Returns the event parameters and whether the event was declared as anonymous -fn parse_event_args(mut input: &str) -> Result<(Vec, bool), ParseError> { - let mut anonymous = false; - if input.ends_with("anonymous") { - anonymous = true; - input = input[..input.len()-9].trim_end(); - } - +/// Returns the event parameters +fn parse_event_args(mut input: &str) -> Result, ParseError> { input = input.trim().strip_suffix(')').ok_or(ParseError::ParseError(super::Error::InvalidData))?; - input = input.trim_end_matches('\\'); let mut params = Vec::new(); if input.is_empty() { - return Ok((params, anonymous)) + return Ok(params); } for arg in input.split(',') { @@ -148,10 +143,68 @@ fn parse_event_args(mut input: &str) -> Result<(Vec, bool), ParseErr } ) } - Ok((params, anonymous)) + Ok(params) } +fn parse_function2(mut input: &str) -> Result { + input = input.trim(); + if !input.starts_with("function ") { + return Err(ParseError::ParseError(super::Error::InvalidData)); + } + input = &input[9..]; + let name = parse_identifier(&mut input)?; + if name.is_empty() { + return Err(ParseError::ParseError(super::Error::InvalidName(input.to_owned()))); + } + + let mut iter = input.split(" returns"); + + let parens = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?.trim_end(); + + let mut parens_iter = parens.rsplitn(2, ')'); + let mut modifiers = parens_iter.next(); + + let input_params = if let Some(args) = parens_iter.next() { + args + } else { + modifiers.take().ok_or(ParseError::ParseError(super::Error::InvalidData))? + }.trim_start().strip_prefix('(').ok_or(ParseError::ParseError(super::Error::InvalidData))?; + + + let inputs = input_params.split(',').filter(|s|!s.is_empty()).map(parse_param2).collect::,_>>()?; + + let outputs = if let Some(params) = iter.next() { + let params = params.trim().strip_prefix('(').and_then(|s|s.strip_suffix(')')).ok_or(ParseError::ParseError(super::Error::InvalidData))?; + params.split(',').filter(|s|!s.is_empty()).map(parse_param2).collect::,_>>()? + } else { + Vec::new() + }; + + let state_mutability = if let Some(modifiers) = modifiers { + if modifiers.contains("pure") { + StateMutability::Pure + } else if modifiers.contains("view") { + StateMutability::View + } else if modifiers.contains("payable") { + StateMutability::Payable + } else { + StateMutability::NonPayable + } + } else { + StateMutability::NonPayable + }; + + #[allow(deprecated)] + Ok(Function { + name, + inputs, + outputs, + state_mutability, + constant: false, + }) +} + fn parse2(input: &[&str]) -> Result { let mut abi = Abi { constructor: None, @@ -162,23 +215,24 @@ fn parse2(input: &[&str]) -> Result { }; for line in input { - let line = line.trim(); - let (prefix, mut remainder) = line.split_once(' ').ok_or(ParseError::ParseError(super::Error::InvalidData))?; - match prefix { - "event" => { - let event = parse_event2(&mut remainder)?; - abi.events - .entry(event.name.clone()) - .or_default() - .push(event); - } - "function" => {} - "constructor" => {} - _ => return Err(ParseError::ParseError(super::Error::InvalidData)) + let line = line.trim_start(); + if line.starts_with("function") { + let function = parse_function2(&line)?; + abi.functions + .entry(function.name.clone()) + .or_default() + .push(function); + } else if line.starts_with("event") { + let event = parse_event2(line)?; + abi.events + .entry(event.name.clone()) + .or_default() + .push(event); + } else if line.starts_with("constructor") {} else { + return Err(ParseError::ParseError(super::Error::InvalidData)); } } - Ok(abi) } @@ -286,6 +340,22 @@ fn parse_function(fn_string: &str) -> Result { }) } +fn parse_param2(param: &str) -> Result { + let mut iter = param.trim().rsplitn(3, is_whitespace); + + let name = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?; + + if let Some(ty) = iter.last() { + if name == "memory" || name == "calldata" { + Ok(Param { name: "".to_owned(), kind: Reader::read(ty)? }) + } else { + Ok(Param { name: name.to_owned(), kind: Reader::read(ty)? }) + } + } else { + Ok(Param { name: "".to_owned(), kind: Reader::read(name)? }) + } +} + // address x fn parse_param(param: &str) -> Result { let mut param = param @@ -321,6 +391,19 @@ pub enum ParseError { mod tests { use super::*; + #[test] + fn parses_approve2() { + let fn_str = "function approve(address _spender, uint256 value) external returns(bool)"; + let parsed = parse_function2(fn_str).unwrap(); + assert_eq!(parsed.name, "approve"); + assert_eq!(parsed.inputs[0].name, "_spender"); + assert_eq!(parsed.inputs[0].kind, ParamType::Address, ); + assert_eq!(parsed.inputs[1].name, "value"); + assert_eq!(parsed.inputs[1].kind, ParamType::Uint(256), ); + assert_eq!(parsed.outputs[0].name, ""); + assert_eq!(parsed.outputs[0].kind, ParamType::Bool); + } + #[test] fn parses_approve() { let fn_str = "function approve(address _spender, uint256 value) external returns(bool)"; @@ -334,6 +417,20 @@ mod tests { assert_eq!(parsed.outputs[0].kind, ParamType::Bool); } + #[test] + fn parses_function_arguments_return2() { + let fn_str = "function foo(uint32[] memory x) external view returns (address)"; + let parsed = parse_function2(fn_str).unwrap(); + assert_eq!(parsed.name, "foo"); + assert_eq!(parsed.inputs[0].name, "x"); + assert_eq!( + parsed.inputs[0].kind, + ParamType::Array(Box::new(ParamType::Uint(32))) + ); + assert_eq!(parsed.outputs[0].name, ""); + assert_eq!(parsed.outputs[0].kind, ParamType::Address); + } + #[test] fn parses_function_arguments_return() { let fn_str = "function foo(uint32[] memory x) external view returns (address)"; @@ -348,6 +445,36 @@ mod tests { assert_eq!(parsed.outputs[0].kind, ParamType::Address); } + #[test] + fn parses_function_empty2() { + let fn_str = "function foo()"; + let parsed = parse_function2(fn_str).unwrap(); + assert_eq!(parsed.name, "foo"); + assert!(parsed.inputs.is_empty()); + assert!(parsed.outputs.is_empty()); + } + + #[test] + fn parses_function_payable() { + let fn_str = "function foo() public payable"; + let parsed = parse_function2(fn_str).unwrap(); + assert_eq!(parsed.state_mutability, StateMutability::Payable); + } + + #[test] + fn parses_function_view() { + let fn_str = "function foo() external view"; + let parsed = parse_function2(fn_str).unwrap(); + assert_eq!(parsed.state_mutability, StateMutability::View); + } + + #[test] + fn parses_function_pure() { + let fn_str = "function foo() pure"; + let parsed = parse_function2(fn_str).unwrap(); + assert_eq!(parsed.state_mutability, StateMutability::Pure); + } + #[test] fn parses_function_empty() { let fn_str = "function foo()"; @@ -458,6 +585,22 @@ mod tests { ); } + #[test] + fn can_parse_functions2() { + [ + "function foo(uint256[] memory x) external view returns (address)", + "function bar(uint256[] memory x) returns (address)", + "function bar(uint256[] memory x, uint32 y) returns (address, uint256)", + "function foo(address[] memory, bytes memory) returns (bytes memory)", + "function bar(uint256[] memory x)", + "function bar()", + ] + .iter() + .for_each(|x| { + parse_function2(x).unwrap(); + }); + } + #[test] fn can_parse_functions() { [ From 4419d4bff96622dc48a50681eec1d0f5d5203c01 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 11 Mar 2021 21:08:28 +0100 Subject: [PATCH 3/8] refactor: add constructor parser --- ethers-core/src/abi/human_readable.rs | 54 ++++++++++++++++++--------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/ethers-core/src/abi/human_readable.rs b/ethers-core/src/abi/human_readable.rs index 7e5816c85..9fb45d000 100644 --- a/ethers-core/src/abi/human_readable.rs +++ b/ethers-core/src/abi/human_readable.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use thiserror::Error; use super::{ - Abi, Event, EventParam, Function, Param, param_type::Reader, ParamType, StateMutability, + Abi, Event, EventParam, Function, Param, param_type::Reader, ParamType, StateMutability, Constructor }; /// Parses a "human readable abi" string vector @@ -74,7 +74,7 @@ fn parse_event2(mut event: &str) -> Result { if !event.starts_with("event ") { return Err(ParseError::ParseError(super::Error::InvalidData)); } - event = &event[6..]; + event = &event[5..]; let name = parse_identifier(&mut event)?; if name.is_empty() { @@ -152,7 +152,7 @@ fn parse_function2(mut input: &str) -> Result { if !input.starts_with("function ") { return Err(ParseError::ParseError(super::Error::InvalidData)); } - input = &input[9..]; + input = &input[8..]; let name = parse_identifier(&mut input)?; if name.is_empty() { return Err(ParseError::ParseError(super::Error::InvalidName(input.to_owned()))); @@ -181,19 +181,7 @@ fn parse_function2(mut input: &str) -> Result { Vec::new() }; - let state_mutability = if let Some(modifiers) = modifiers { - if modifiers.contains("pure") { - StateMutability::Pure - } else if modifiers.contains("view") { - StateMutability::View - } else if modifiers.contains("payable") { - StateMutability::Payable - } else { - StateMutability::NonPayable - } - } else { - StateMutability::NonPayable - }; + let state_mutability = modifiers.map(detect_state_mutability).unwrap_or_default(); #[allow(deprecated)] Ok(Function { @@ -205,6 +193,23 @@ fn parse_function2(mut input: &str) -> Result { }) } +fn parse_constructor(mut input: &str) -> Result { + input = input.trim(); + if !input.starts_with("constructor") { + return Err(ParseError::ParseError(super::Error::InvalidData)); + } + input = input[11..].trim_start().strip_prefix('(').ok_or(ParseError::ParseError(super::Error::InvalidData))?; + + let params = input.rsplitn(2, ')').last().ok_or(ParseError::ParseError(super::Error::InvalidData))?; + + let inputs = params.split(',').filter(|s|!s.is_empty()).map(parse_param2).collect::,_>>()?; + + #[allow(deprecated)] + Ok(Constructor { + inputs, + }) +} + fn parse2(input: &[&str]) -> Result { let mut abi = Abi { constructor: None, @@ -228,7 +233,11 @@ fn parse2(input: &[&str]) -> Result { .entry(event.name.clone()) .or_default() .push(event); - } else if line.starts_with("constructor") {} else { + } else if line.starts_with("constructor") { + abi.constructor = Some(parse_constructor(line)?); + } else { + + return Err(ParseError::ParseError(super::Error::InvalidData)); } } @@ -236,6 +245,17 @@ fn parse2(input: &[&str]) -> Result { Ok(abi) } +fn detect_state_mutability(s: &str) -> StateMutability { + if s.contains("pure") { + StateMutability::Pure + } else if s.contains("view") { + StateMutability::View + } else if s.contains("payable") { + StateMutability::Payable + } else { + StateMutability::NonPayable + } +} fn is_first_ident_char(c: char) -> bool { matches!(c, 'a'..='z' | 'A'..='Z' | '_') From 218c704760f82975ec78bffdb4c25fe939706863 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 11 Mar 2021 22:23:19 +0100 Subject: [PATCH 4/8] refactor: replace parsers --- ethers-core/src/abi/human_readable.rs | 478 ++++++++------------------ 1 file changed, 146 insertions(+), 332 deletions(-) diff --git a/ethers-core/src/abi/human_readable.rs b/ethers-core/src/abi/human_readable.rs index 9fb45d000..ab9803e82 100644 --- a/ethers-core/src/abi/human_readable.rs +++ b/ethers-core/src/abi/human_readable.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use thiserror::Error; use super::{ - Abi, Event, EventParam, Function, Param, param_type::Reader, ParamType, StateMutability, Constructor + param_type::Reader, Abi, Constructor, Event, EventParam, Function, Param, StateMutability, }; /// Parses a "human readable abi" string vector @@ -24,23 +24,24 @@ pub fn parse(input: &[&str]) -> Result { fallback: false, }; - for line in input { - if line.contains("function") { + for mut line in input.iter().map(|s| escape_quotes(s)) { + line = line.trim_start(); + if line.starts_with("function") { let function = parse_function(&line)?; abi.functions .entry(function.name.clone()) .or_default() .push(function); - } else if line.contains("event") { - let event = parse_event(&line)?; + } else if line.starts_with("event") { + let event = parse_event(line)?; abi.events .entry(event.name.clone()) .or_default() .push(event); - } else if line.starts_with("struct") { - panic!("Got tuple"); + } else if line.starts_with("constructor") { + abi.constructor = Some(parse_constructor(line)?); } else { - panic!("unknown sig") + return Err(ParseError::ParseError(super::Error::InvalidData)); } } @@ -51,7 +52,9 @@ pub fn parse(input: &[&str]) -> Result { fn parse_identifier(input: &mut &str) -> Result { let mut chars = input.trim_start().chars(); let mut name = String::new(); - let c = chars.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?; + let c = chars + .next() + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; if is_first_ident_char(c) { name.push(c); loop { @@ -69,7 +72,7 @@ fn parse_identifier(input: &mut &str) -> Result { } /// Parses a solidity event declaration from `event (args*) anonymous?` -fn parse_event2(mut event: &str) -> Result { +fn parse_event(mut event: &str) -> Result { event = event.trim(); if !event.starts_with("event ") { return Err(ParseError::ParseError(super::Error::InvalidData)); @@ -78,7 +81,9 @@ fn parse_event2(mut event: &str) -> Result { let name = parse_identifier(&mut event)?; if name.is_empty() { - return Err(ParseError::ParseError(super::Error::InvalidName(event.to_owned()))); + return Err(ParseError::ParseError(super::Error::InvalidName( + event.to_owned(), + ))); } let mut chars = event.chars(); @@ -112,7 +117,10 @@ fn parse_event2(mut event: &str) -> Result { /// Returns the event parameters fn parse_event_args(mut input: &str) -> Result, ParseError> { - input = input.trim().strip_suffix(')').ok_or(ParseError::ParseError(super::Error::InvalidData))?; + input = input + .trim() + .strip_suffix(')') + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; let mut params = Vec::new(); @@ -122,9 +130,13 @@ fn parse_event_args(mut input: &str) -> Result, ParseError> { for arg in input.split(',') { let mut iter = arg.trim().rsplitn(3, is_whitespace); - let name = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let mid = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let mut kind = None; + let name = iter + .next() + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; + let mid = iter + .next() + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; + let kind; let mut indexed = false; if mid == "indexed" { indexed = true; @@ -135,19 +147,16 @@ fn parse_event_args(mut input: &str) -> Result, ParseError> { return Err(ParseError::ParseError(super::Error::InvalidData)); } } - params.push( - EventParam { - name: name.to_owned(), - kind: kind.ok_or(ParseError::ParseError(super::Error::InvalidData))??, - indexed, - } - ) + params.push(EventParam { + name: name.to_owned(), + kind: kind.ok_or(ParseError::ParseError(super::Error::InvalidData))??, + indexed, + }) } Ok(params) } - -fn parse_function2(mut input: &str) -> Result { +fn parse_function(mut input: &str) -> Result { input = input.trim(); if !input.starts_with("function ") { return Err(ParseError::ParseError(super::Error::InvalidData)); @@ -155,12 +164,17 @@ fn parse_function2(mut input: &str) -> Result { input = &input[8..]; let name = parse_identifier(&mut input)?; if name.is_empty() { - return Err(ParseError::ParseError(super::Error::InvalidName(input.to_owned()))); + return Err(ParseError::ParseError(super::Error::InvalidName( + input.to_owned(), + ))); } let mut iter = input.split(" returns"); - let parens = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?.trim_end(); + let parens = iter + .next() + .ok_or(ParseError::ParseError(super::Error::InvalidData))? + .trim_end(); let mut parens_iter = parens.rsplitn(2, ')'); let mut modifiers = parens_iter.next(); @@ -168,15 +182,31 @@ fn parse_function2(mut input: &str) -> Result { let input_params = if let Some(args) = parens_iter.next() { args } else { - modifiers.take().ok_or(ParseError::ParseError(super::Error::InvalidData))? - }.trim_start().strip_prefix('(').ok_or(ParseError::ParseError(super::Error::InvalidData))?; - + modifiers + .take() + .ok_or(ParseError::ParseError(super::Error::InvalidData))? + } + .trim_start() + .strip_prefix('(') + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let inputs = input_params.split(',').filter(|s|!s.is_empty()).map(parse_param2).collect::,_>>()?; + let inputs = input_params + .split(',') + .filter(|s| !s.is_empty()) + .map(parse_param) + .collect::, _>>()?; let outputs = if let Some(params) = iter.next() { - let params = params.trim().strip_prefix('(').and_then(|s|s.strip_suffix(')')).ok_or(ParseError::ParseError(super::Error::InvalidData))?; - params.split(',').filter(|s|!s.is_empty()).map(parse_param2).collect::,_>>()? + let params = params + .trim() + .strip_prefix('(') + .and_then(|s| s.strip_suffix(')')) + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; + params + .split(',') + .filter(|s| !s.is_empty()) + .map(parse_param) + .collect::, _>>()? } else { Vec::new() }; @@ -184,7 +214,7 @@ fn parse_function2(mut input: &str) -> Result { let state_mutability = modifiers.map(detect_state_mutability).unwrap_or_default(); #[allow(deprecated)] - Ok(Function { + Ok(Function { name, inputs, outputs, @@ -198,51 +228,24 @@ fn parse_constructor(mut input: &str) -> Result { if !input.starts_with("constructor") { return Err(ParseError::ParseError(super::Error::InvalidData)); } - input = input[11..].trim_start().strip_prefix('(').ok_or(ParseError::ParseError(super::Error::InvalidData))?; + input = input[11..] + .trim_start() + .strip_prefix('(') + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let params = input.rsplitn(2, ')').last().ok_or(ParseError::ParseError(super::Error::InvalidData))?; + let params = input + .rsplitn(2, ')') + .last() + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let inputs = params.split(',').filter(|s|!s.is_empty()).map(parse_param2).collect::,_>>()?; + let inputs = params + .split(',') + .filter(|s| !s.is_empty()) + .map(parse_param) + .collect::, _>>()?; #[allow(deprecated)] - Ok(Constructor { - inputs, - }) -} - -fn parse2(input: &[&str]) -> Result { - let mut abi = Abi { - constructor: None, - functions: HashMap::new(), - events: HashMap::new(), - receive: false, - fallback: false, - }; - - for line in input { - let line = line.trim_start(); - if line.starts_with("function") { - let function = parse_function2(&line)?; - abi.functions - .entry(function.name.clone()) - .or_default() - .push(function); - } else if line.starts_with("event") { - let event = parse_event2(line)?; - abi.events - .entry(event.name.clone()) - .or_default() - .push(event); - } else if line.starts_with("constructor") { - abi.constructor = Some(parse_constructor(line)?); - } else { - - - return Err(ParseError::ParseError(super::Error::InvalidData)); - } - } - - Ok(abi) + Ok(Constructor { inputs }) } fn detect_state_mutability(s: &str) -> StateMutability { @@ -257,145 +260,47 @@ fn detect_state_mutability(s: &str) -> StateMutability { } } -fn is_first_ident_char(c: char) -> bool { - matches!(c, 'a'..='z' | 'A'..='Z' | '_') -} - -fn is_ident_char(c: char) -> bool { - matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_') -} - -fn is_whitespace(c: char) -> bool { - matches!(c, ' ' | '\t' ) -} - -fn parse_event(event: &str) -> Result { - let split: Vec<&str> = event.split("event ").collect(); - let split: Vec<&str> = split[1].split('(').collect(); - let name = split[0].trim_end(); - let rest = split[1]; - - let args = rest.replace(")", ""); - let anonymous = rest.contains("anonymous"); - - let inputs = if args.contains(',') { - let args: Vec<&str> = args.split(", ").collect(); - args.iter() - .map(|arg| parse_event_arg(arg)) - .collect::, _>>()? - } else { - vec![] - }; - - Ok(Event { - name: name.to_owned(), - anonymous, - inputs, - }) -} - -// Parses an event's argument as indexed if neded -fn parse_event_arg(param: &str) -> Result { - let tokens: Vec<&str> = param.split(' ').collect(); - let kind: ParamType = Reader::read(tokens[0])?; - let (name, indexed) = if tokens.len() == 2 { - (tokens[1], false) - } else { - (tokens[2], true) - }; - - Ok(EventParam { - name: name.to_owned(), - kind, - indexed, - }) -} - -fn parse_function(fn_string: &str) -> Result { - let fn_string = fn_string.to_owned(); - let delim = if fn_string.starts_with("function ") { - "function " - } else { - " " - }; - let split: Vec<&str> = fn_string.split(delim).collect(); - let split: Vec<&str> = split[1].split('(').collect(); - - // function name is the first char - let fn_name = split[0]; - - // internal args - let args: Vec<&str> = split[1].split(')').collect(); - let args: Vec<&str> = args[0].split(", ").collect(); - - let inputs = args - .into_iter() - .filter(|x| !x.is_empty()) - .filter(|x| !x.contains("returns")) - .map(|x| parse_param(x)) - .collect::, _>>()?; - - // return value - let outputs: Vec = if split.len() > 2 { - let ret = split[2].strip_suffix(")").expect("no right paren"); - let ret: Vec<&str> = ret.split(", ").collect(); - - ret.into_iter() - // remove modifiers etc - .filter(|x| !x.is_empty()) - .map(|x| parse_param(x)) - .collect::, _>>()? - } else { - vec![] - }; - - #[allow(deprecated)] - Ok(Function { - name: fn_name.to_owned(), - inputs, - outputs, - // this doesn't really matter - state_mutability: StateMutability::NonPayable, - constant: false, - }) -} - -fn parse_param2(param: &str) -> Result { +fn parse_param(param: &str) -> Result { let mut iter = param.trim().rsplitn(3, is_whitespace); - let name = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?; + let name = iter + .next() + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; if let Some(ty) = iter.last() { if name == "memory" || name == "calldata" { - Ok(Param { name: "".to_owned(), kind: Reader::read(ty)? }) + Ok(Param { + name: "".to_owned(), + kind: Reader::read(ty)?, + }) } else { - Ok(Param { name: name.to_owned(), kind: Reader::read(ty)? }) + Ok(Param { + name: name.to_owned(), + kind: Reader::read(ty)?, + }) } } else { - Ok(Param { name: "".to_owned(), kind: Reader::read(name)? }) + Ok(Param { + name: "".to_owned(), + kind: Reader::read(name)?, + }) } } -// address x -fn parse_param(param: &str) -> Result { - let mut param = param - .split(' ') - .filter(|x| !x.contains("memory") || !x.contains("calldata")); - - let kind = param.next().ok_or(ParseError::Kind)?; - let kind: ParamType = Reader::read(kind).unwrap(); - - // strip memory/calldata from the name - // e.g. uint256[] memory x - let mut name = param.next().unwrap_or_default(); - if name == "memory" || name == "calldata" { - name = param.next().unwrap_or_default(); - } +fn is_first_ident_char(c: char) -> bool { + matches!(c, 'a'..='z' | 'A'..='Z' | '_') +} - Ok(Param { - name: name.to_owned(), - kind, - }) +fn is_ident_char(c: char) -> bool { + matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_') +} + +fn is_whitespace(c: char) -> bool { + matches!(c, ' ' | '\t') +} + +fn escape_quotes(input: &str) -> &str { + input.trim_matches(is_whitespace).trim_matches('\"') } #[derive(Error, Debug)] @@ -410,19 +315,7 @@ pub enum ParseError { #[cfg(test)] mod tests { use super::*; - - #[test] - fn parses_approve2() { - let fn_str = "function approve(address _spender, uint256 value) external returns(bool)"; - let parsed = parse_function2(fn_str).unwrap(); - assert_eq!(parsed.name, "approve"); - assert_eq!(parsed.inputs[0].name, "_spender"); - assert_eq!(parsed.inputs[0].kind, ParamType::Address, ); - assert_eq!(parsed.inputs[1].name, "value"); - assert_eq!(parsed.inputs[1].kind, ParamType::Uint(256), ); - assert_eq!(parsed.outputs[0].name, ""); - assert_eq!(parsed.outputs[0].kind, ParamType::Bool); - } + use crate::abi::ParamType; #[test] fn parses_approve() { @@ -430,27 +323,13 @@ mod tests { let parsed = parse_function(fn_str).unwrap(); assert_eq!(parsed.name, "approve"); assert_eq!(parsed.inputs[0].name, "_spender"); - assert_eq!(parsed.inputs[0].kind, ParamType::Address, ); + assert_eq!(parsed.inputs[0].kind, ParamType::Address,); assert_eq!(parsed.inputs[1].name, "value"); - assert_eq!(parsed.inputs[1].kind, ParamType::Uint(256), ); + assert_eq!(parsed.inputs[1].kind, ParamType::Uint(256),); assert_eq!(parsed.outputs[0].name, ""); assert_eq!(parsed.outputs[0].kind, ParamType::Bool); } - #[test] - fn parses_function_arguments_return2() { - let fn_str = "function foo(uint32[] memory x) external view returns (address)"; - let parsed = parse_function2(fn_str).unwrap(); - assert_eq!(parsed.name, "foo"); - assert_eq!(parsed.inputs[0].name, "x"); - assert_eq!( - parsed.inputs[0].kind, - ParamType::Array(Box::new(ParamType::Uint(32))) - ); - assert_eq!(parsed.outputs[0].name, ""); - assert_eq!(parsed.outputs[0].kind, ParamType::Address); - } - #[test] fn parses_function_arguments_return() { let fn_str = "function foo(uint32[] memory x) external view returns (address)"; @@ -466,9 +345,9 @@ mod tests { } #[test] - fn parses_function_empty2() { + fn parses_function_empty() { let fn_str = "function foo()"; - let parsed = parse_function2(fn_str).unwrap(); + let parsed = parse_function(fn_str).unwrap(); assert_eq!(parsed.name, "foo"); assert!(parsed.inputs.is_empty()); assert!(parsed.outputs.is_empty()); @@ -477,65 +356,28 @@ mod tests { #[test] fn parses_function_payable() { let fn_str = "function foo() public payable"; - let parsed = parse_function2(fn_str).unwrap(); + let parsed = parse_function(fn_str).unwrap(); assert_eq!(parsed.state_mutability, StateMutability::Payable); } #[test] fn parses_function_view() { let fn_str = "function foo() external view"; - let parsed = parse_function2(fn_str).unwrap(); + let parsed = parse_function(fn_str).unwrap(); assert_eq!(parsed.state_mutability, StateMutability::View); } #[test] fn parses_function_pure() { let fn_str = "function foo() pure"; - let parsed = parse_function2(fn_str).unwrap(); - assert_eq!(parsed.state_mutability, StateMutability::Pure); - } - - #[test] - fn parses_function_empty() { - let fn_str = "function foo()"; let parsed = parse_function(fn_str).unwrap(); - assert_eq!(parsed.name, "foo"); - assert!(parsed.inputs.is_empty()); - assert!(parsed.outputs.is_empty()); - } - - #[test] - fn parses_event2() { - assert_eq!( - parse_event2(&mut "event Foo (address indexed x, uint y, bytes32[] z)").unwrap(), - Event { - anonymous: false, - name: "Foo".to_owned(), - inputs: vec![ - EventParam { - name: "x".to_owned(), - kind: ParamType::Address, - indexed: true, - }, - EventParam { - name: "y".to_owned(), - kind: ParamType::Uint(256), - indexed: false, - }, - EventParam { - name: "z".to_owned(), - kind: ParamType::Array(Box::new(ParamType::FixedBytes(32))), - indexed: false, - }, - ], - } - ); + assert_eq!(parsed.state_mutability, StateMutability::Pure); } #[test] fn parses_event() { assert_eq!( - parse_event("event Foo (address indexed x, uint y, bytes32[] z)").unwrap(), + parse_event(&mut "event Foo (address indexed x, uint y, bytes32[] z)").unwrap(), Event { anonymous: false, name: "Foo".to_owned(), @@ -560,22 +402,10 @@ mod tests { ); } - #[test] - fn parses_anonymous_event2() { - assert_eq!( - parse_event2(&mut "event Foo() anonymous").unwrap(), - Event { - anonymous: true, - name: "Foo".to_owned(), - inputs: vec![], - } - ); - } - #[test] fn parses_anonymous_event() { assert_eq!( - parse_event("event Foo() anonymous").unwrap(), + parse_event(&mut "event Foo() anonymous").unwrap(), Event { anonymous: true, name: "Foo".to_owned(), @@ -584,42 +414,26 @@ mod tests { ); } - #[test] - fn parse_event_input() { - assert_eq!( - parse_event_arg("address indexed x").unwrap(), - EventParam { - name: "x".to_owned(), - kind: ParamType::Address, - indexed: true, - } - ); - - assert_eq!( - parse_event_arg("address x").unwrap(), - EventParam { - name: "x".to_owned(), - kind: ParamType::Address, - indexed: false, - } - ); - } - - #[test] - fn can_parse_functions2() { - [ - "function foo(uint256[] memory x) external view returns (address)", - "function bar(uint256[] memory x) returns (address)", - "function bar(uint256[] memory x, uint32 y) returns (address, uint256)", - "function foo(address[] memory, bytes memory) returns (bytes memory)", - "function bar(uint256[] memory x)", - "function bar()", - ] - .iter() - .for_each(|x| { - parse_function2(x).unwrap(); - }); - } + // #[test] + // fn parse_event_input() { + // assert_eq!( + // parse_event_arg("address indexed x").unwrap(), + // EventParam { + // name: "x".to_owned(), + // kind: ParamType::Address, + // indexed: true, + // } + // ); + // + // assert_eq!( + // parse_event_arg("address x").unwrap(), + // EventParam { + // name: "x".to_owned(), + // kind: ParamType::Address, + // indexed: false, + // } + // ); + // } #[test] fn can_parse_functions() { @@ -631,10 +445,10 @@ mod tests { "function bar(uint256[] memory x)", "function bar()", ] - .iter() - .for_each(|x| { - parse_function(x).unwrap(); - }); + .iter() + .for_each(|x| { + parse_function(x).unwrap(); + }); } #[test] @@ -647,18 +461,18 @@ mod tests { "bytes32[] memory", "bytes32[] memory z", ] - .iter() - .for_each(|x| { - parse_param(x).unwrap(); - }); + .iter() + .for_each(|x| { + parse_param(x).unwrap(); + }); } #[test] fn can_read_backslashes() { parse(&[ "\"function setValue(string)\"", - "\"function getValue() external view (string)\"", + "\"function getValue() external view returns(string)\"", ]) - .unwrap(); + .unwrap(); } } From 910b7cc4f7dd6e33442167b430bd36dfd52ed435 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 11 Mar 2021 22:45:00 +0100 Subject: [PATCH 5/8] style: extract event arg parsing into separate method --- ethers-core/src/abi/human_readable.rs | 112 +++++++++++++------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/ethers-core/src/abi/human_readable.rs b/ethers-core/src/abi/human_readable.rs index ab9803e82..d97cf2ba1 100644 --- a/ethers-core/src/abi/human_readable.rs +++ b/ethers-core/src/abi/human_readable.rs @@ -98,7 +98,19 @@ fn parse_event(mut event: &str) -> Result { anonymous = true; event = event[..event.len() - 9].trim_end(); } - let inputs = parse_event_args(event)?; + event = event + .trim() + .strip_suffix(')') + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; + + let inputs = if event.is_empty() { + Vec::new() + } else { + event + .split(',') + .map(parse_event_arg) + .collect::, _>>()? + }; return Ok(Event { name, anonymous, @@ -115,45 +127,31 @@ fn parse_event(mut event: &str) -> Result { } } -/// Returns the event parameters -fn parse_event_args(mut input: &str) -> Result, ParseError> { - input = input - .trim() - .strip_suffix(')') +/// Parse a single event param +fn parse_event_arg(input: &str) -> Result { + let mut iter = input.trim().rsplitn(3, is_whitespace); + let name = iter + .next() .ok_or(ParseError::ParseError(super::Error::InvalidData))?; - - let mut params = Vec::new(); - - if input.is_empty() { - return Ok(params); - } - - for arg in input.split(',') { - let mut iter = arg.trim().rsplitn(3, is_whitespace); - let name = iter - .next() - .ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let mid = iter - .next() - .ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let kind; - let mut indexed = false; - if mid == "indexed" { - indexed = true; - kind = iter.next().map(Reader::read); - } else { - kind = Some(Reader::read(mid)); - if iter.next().is_some() { - return Err(ParseError::ParseError(super::Error::InvalidData)); - } + let mid = iter + .next() + .ok_or(ParseError::ParseError(super::Error::InvalidData))?; + let kind; + let mut indexed = false; + if mid == "indexed" { + indexed = true; + kind = iter.next().map(Reader::read); + } else { + kind = Some(Reader::read(mid)); + if iter.next().is_some() { + return Err(ParseError::ParseError(super::Error::InvalidData)); } - params.push(EventParam { - name: name.to_owned(), - kind: kind.ok_or(ParseError::ParseError(super::Error::InvalidData))??, - indexed, - }) } - Ok(params) + Ok(EventParam { + name: name.to_owned(), + kind: kind.ok_or(ParseError::ParseError(super::Error::InvalidData))??, + indexed, + }) } fn parse_function(mut input: &str) -> Result { @@ -414,26 +412,26 @@ mod tests { ); } - // #[test] - // fn parse_event_input() { - // assert_eq!( - // parse_event_arg("address indexed x").unwrap(), - // EventParam { - // name: "x".to_owned(), - // kind: ParamType::Address, - // indexed: true, - // } - // ); - // - // assert_eq!( - // parse_event_arg("address x").unwrap(), - // EventParam { - // name: "x".to_owned(), - // kind: ParamType::Address, - // indexed: false, - // } - // ); - // } + #[test] + fn parse_event_input() { + assert_eq!( + parse_event_arg("address indexed x").unwrap(), + EventParam { + name: "x".to_owned(), + kind: ParamType::Address, + indexed: true, + } + ); + + assert_eq!( + parse_event_arg("address x").unwrap(), + EventParam { + name: "x".to_owned(), + kind: ParamType::Address, + indexed: false, + } + ); + } #[test] fn can_parse_functions() { From f3bd1563009973ab3e36c4b0b8e62cd141efc28b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 11 Mar 2021 22:45:17 +0100 Subject: [PATCH 6/8] fix: add missing returns statement --- ethers/examples/contract_human_readable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethers/examples/contract_human_readable.rs b/ethers/examples/contract_human_readable.rs index 3b6b4ca30..ef3c0b521 100644 --- a/ethers/examples/contract_human_readable.rs +++ b/ethers/examples/contract_human_readable.rs @@ -11,7 +11,7 @@ abigen!( SimpleContract, r#"[ function setValue(string) - function getValue() external view (string) + function getValue() external view returns (string) event ValueChanged(address indexed author, string oldValue, string newValue) ]"#, event_derives(serde::Deserialize, serde::Serialize) From c41099a01665510feac050bcf2eb007dc69525eb Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 11 Mar 2021 22:46:54 +0100 Subject: [PATCH 7/8] chore(clippy): make clippy happy --- ethers-core/src/abi/human_readable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethers-core/src/abi/human_readable.rs b/ethers-core/src/abi/human_readable.rs index d97cf2ba1..02c680147 100644 --- a/ethers-core/src/abi/human_readable.rs +++ b/ethers-core/src/abi/human_readable.rs @@ -113,8 +113,8 @@ fn parse_event(mut event: &str) -> Result { }; return Ok(Event { name, - anonymous, inputs, + anonymous, }); } Some(' ') | Some('\t') => { From c6c1af16dfc91cff85a46b2267f32e4c01566ec5 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 12 Mar 2021 12:22:46 +0100 Subject: [PATCH 8/8] fix: support unnamed event argument parsing --- ethers-core/src/abi/human_readable.rs | 79 ++++++++++++++++++++------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/ethers-core/src/abi/human_readable.rs b/ethers-core/src/abi/human_readable.rs index 02c680147..990152481 100644 --- a/ethers-core/src/abi/human_readable.rs +++ b/ethers-core/src/abi/human_readable.rs @@ -130,28 +130,38 @@ fn parse_event(mut event: &str) -> Result { /// Parse a single event param fn parse_event_arg(input: &str) -> Result { let mut iter = input.trim().rsplitn(3, is_whitespace); - let name = iter - .next() - .ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let mid = iter + let mut indexed = false; + let mut name = iter .next() .ok_or(ParseError::ParseError(super::Error::InvalidData))?; - let kind; - let mut indexed = false; - if mid == "indexed" { - indexed = true; - kind = iter.next().map(Reader::read); - } else { - kind = Some(Reader::read(mid)); - if iter.next().is_some() { - return Err(ParseError::ParseError(super::Error::InvalidData)); + + if let Some(mid) = iter.next() { + let kind; + if let Some(ty) = iter.next() { + if mid != "indexed" { + return Err(ParseError::ParseError(super::Error::InvalidData)); + } + indexed = true; + kind = Reader::read(ty)?; + } else { + if name == "indexed" { + indexed = true; + name = ""; + } + kind = Reader::read(mid)?; } + Ok(EventParam { + name: name.to_owned(), + kind, + indexed, + }) + } else { + Ok(EventParam { + name: "".to_owned(), + indexed, + kind: Reader::read(name)?, + }) } - Ok(EventParam { - name: name.to_owned(), - kind: kind.ok_or(ParseError::ParseError(super::Error::InvalidData))??, - indexed, - }) } fn parse_function(mut input: &str) -> Result { @@ -242,7 +252,6 @@ fn parse_constructor(mut input: &str) -> Result { .map(parse_param) .collect::, _>>()?; - #[allow(deprecated)] Ok(Constructor { inputs }) } @@ -412,6 +421,38 @@ mod tests { ); } + #[test] + fn parses_unnamed_event() { + assert_eq!( + parse_event(&mut "event Foo(address)").unwrap(), + Event { + anonymous: false, + name: "Foo".to_owned(), + inputs: vec![EventParam { + name: "".to_owned(), + kind: ParamType::Address, + indexed: false, + }], + } + ); + } + + #[test] + fn parses_unnamed_indexed_event() { + assert_eq!( + parse_event(&mut "event Foo(address indexed)").unwrap(), + Event { + anonymous: false, + name: "Foo".to_owned(), + inputs: vec![EventParam { + name: "".to_owned(), + kind: ParamType::Address, + indexed: true, + }], + } + ); + } + #[test] fn parse_event_input() { assert_eq!(