2020-10-29 07:48:24 +00:00
|
|
|
use super::{
|
|
|
|
param_type::Reader, Abi, Event, EventParam, Function, Param, ParamType, StateMutability,
|
|
|
|
};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
/// Parses a "human readable abi" string vector
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use ethers::abi::parse_abi;
|
|
|
|
///
|
|
|
|
/// let abi = parse_abi(&[
|
|
|
|
/// "function x() external view returns (uint256)",
|
|
|
|
/// ]).unwrap();
|
|
|
|
/// ```
|
|
|
|
pub fn parse(input: &[&str]) -> Result<Abi, ParseError> {
|
|
|
|
let mut abi = Abi {
|
|
|
|
constructor: None,
|
|
|
|
functions: HashMap::new(),
|
|
|
|
events: HashMap::new(),
|
|
|
|
receive: false,
|
|
|
|
fallback: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
for line in input {
|
|
|
|
if line.contains("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)?;
|
|
|
|
abi.events
|
|
|
|
.entry(event.name.clone())
|
|
|
|
.or_default()
|
|
|
|
.push(event);
|
|
|
|
} else if line.starts_with("struct") {
|
|
|
|
panic!("Got tuple");
|
|
|
|
} else {
|
|
|
|
panic!("unknown sig")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(abi)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_event(event: &str) -> Result<Event, ParseError> {
|
|
|
|
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::<Result<Vec<EventParam>, _>>()?
|
|
|
|
} 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<EventParam, ParseError> {
|
|
|
|
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<Function, ParseError> {
|
|
|
|
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();
|
2020-10-30 10:15:34 +00:00
|
|
|
|
2020-10-29 07:48:24 +00:00
|
|
|
let inputs = args
|
|
|
|
.into_iter()
|
|
|
|
.filter(|x| !x.is_empty())
|
|
|
|
.filter(|x| !x.contains("returns"))
|
|
|
|
.map(|x| parse_param(x))
|
|
|
|
.collect::<Result<Vec<Param>, _>>()?;
|
|
|
|
|
|
|
|
// return value
|
|
|
|
let outputs: Vec<Param> = 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::<Result<Vec<Param>, _>>()?
|
|
|
|
} else {
|
|
|
|
vec![]
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Function {
|
|
|
|
name: fn_name.to_owned(),
|
|
|
|
inputs,
|
|
|
|
outputs,
|
|
|
|
// this doesn't really matter
|
|
|
|
state_mutability: StateMutability::Nonpayable,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// address x
|
|
|
|
fn parse_param(param: &str) -> Result<Param, ParseError> {
|
|
|
|
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" {
|
2020-10-30 10:15:34 +00:00
|
|
|
name = param.next().unwrap_or_default();
|
2020-10-29 07:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Param {
|
|
|
|
name: name.to_owned(),
|
|
|
|
kind,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Error, Debug)]
|
|
|
|
pub enum ParseError {
|
|
|
|
#[error("expected data type")]
|
|
|
|
Kind,
|
|
|
|
|
|
|
|
#[error(transparent)]
|
|
|
|
ParseError(#[from] super::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn parses_approve() {
|
|
|
|
let fn_str = "function approve(address _spender, uint256 value) external returns(bool)";
|
|
|
|
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[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_function_arguments_return() {
|
|
|
|
let fn_str = "function foo(uint32[] memory x) external view returns (address)";
|
|
|
|
let parsed = parse_function(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_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_event() {
|
|
|
|
assert_eq!(
|
|
|
|
parse_event("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_anonymous_event() {
|
|
|
|
assert_eq!(
|
|
|
|
parse_event("event Foo() anonymous").unwrap(),
|
|
|
|
Event {
|
|
|
|
anonymous: true,
|
|
|
|
name: "Foo".to_owned(),
|
|
|
|
inputs: vec![],
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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() {
|
|
|
|
[
|
|
|
|
"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)",
|
2020-10-30 10:15:34 +00:00
|
|
|
"function foo(address[] memory, bytes memory) returns (bytes memory)",
|
2020-10-29 07:48:24 +00:00
|
|
|
"function bar(uint256[] memory x)",
|
|
|
|
"function bar()",
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|x| {
|
|
|
|
parse_function(x).unwrap();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-30 10:15:34 +00:00
|
|
|
#[test]
|
|
|
|
fn can_parse_params() {
|
|
|
|
[
|
|
|
|
"address x",
|
|
|
|
"address",
|
|
|
|
"bytes memory y",
|
|
|
|
"bytes memory",
|
|
|
|
"bytes32[] memory",
|
|
|
|
"bytes32[] memory z",
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|x| {
|
|
|
|
parse_param(x).unwrap();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-29 07:48:24 +00:00
|
|
|
#[test]
|
|
|
|
fn can_read_backslashes() {
|
|
|
|
parse(&[
|
|
|
|
"\"function setValue(string)\"",
|
|
|
|
"\"function getValue() external view (string)\"",
|
|
|
|
])
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
}
|