Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
test: add event / fn decoding tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gakonst committed Oct 27, 2020
1 parent 222622e commit 4fd2efe
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 13 deletions.
86 changes: 82 additions & 4 deletions ethers-contract/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl BaseContract {
encode_fn(function, args)
}

/// Decodes the provided ABI encoded bytes with the selected function name.
/// Decodes the provided ABI encoded function arguments with the selected function name.
///
/// If the function exists multiple times and you want to use one of the overloaded
/// versions, consider using `decode_with_selector`
Expand All @@ -74,7 +74,7 @@ impl BaseContract {
bytes: impl AsRef<[u8]>,
) -> Result<D, AbiError> {
let function = self.abi.function(name)?;
decode_fn(function, bytes)
decode_fn(function, bytes, true)
}

/// Decodes for a given event name, given the `log.topics` and
Expand All @@ -96,7 +96,7 @@ impl BaseContract {
bytes: impl AsRef<[u8]>,
) -> Result<D, AbiError> {
let function = self.get_from_signature(signature)?;
decode_fn(function, bytes)
decode_fn(function, bytes, true)
}

fn get_from_signature(&self, signature: Selector) -> Result<&Function, AbiError> {
Expand Down Expand Up @@ -155,8 +155,19 @@ pub(crate) fn encode_fn<T: Tokenize>(function: &Function, args: T) -> Result<Byt
pub(crate) fn decode_fn<D: Detokenize>(
function: &Function,
bytes: impl AsRef<[u8]>,
is_input: bool,
) -> Result<D, AbiError> {
let tokens = function.decode_output(bytes.as_ref())?;
let mut bytes = bytes.as_ref();
if bytes.starts_with(&function.selector()) {
bytes = &bytes[4..];
}

let tokens = if is_input {
function.decode_input(bytes.as_ref())?
} else {
function.decode_output(bytes.as_ref())?
};

Ok(D::from_tokens(tokens)?)
}

Expand All @@ -181,3 +192,70 @@ where
})
.collect()
}

#[cfg(test)]
mod tests {
use super::*;
use ethers_core::{abi::parse_abi, types::U256};
use rustc_hex::FromHex;

#[test]
fn can_parse_function_inputs() {
let abi = BaseContract::from(parse_abi(&[
"function approve(address _spender, uint256 value) external view returns (bool, bool)"
]).unwrap());

let spender = "7a250d5630b4cf539739df2c5dacb4c659f2488d"
.parse::<Address>()
.unwrap();
let amount = U256::MAX;

let encoded = abi.encode("approve", (spender, amount)).unwrap();

assert_eq!(encoded.0.to_hex::<String>(), "095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

let (spender2, amount2): (Address, U256) = abi.decode("approve", encoded).unwrap();
assert_eq!(spender, spender2);
assert_eq!(amount, amount2);
}

#[test]
fn can_parse_events() {
let abi = BaseContract::from(
parse_abi(&[
"event Approval(address indexed owner, address indexed spender, uint256 value)",
])
.unwrap(),
);

let topics = vec![
"8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"000000000000000000000000e4e60fdf9bf188fa57b7a5022230363d5bd56d08",
"0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d",
]
.into_iter()
.map(|hash| hash.parse::<H256>().unwrap())
.collect::<Vec<_>>();
let data = Bytes::from(
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
.from_hex::<Vec<u8>>()
.unwrap(),
);

let (owner, spender, value): (Address, Address, U256) =
abi.decode_event("Approval", topics, data).unwrap();
assert_eq!(value, U256::MAX);
assert_eq!(
owner,
"e4e60fdf9bf188fa57b7a5022230363d5bd56d08"
.parse::<Address>()
.unwrap()
);
assert_eq!(
spender,
"7a250d5630b4cf539739df2c5dacb4c659f2488d"
.parse::<Address>()
.unwrap()
);
}
}
3 changes: 2 additions & 1 deletion ethers-contract/src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ where
.await
.map_err(ContractError::MiddlewareError)?;

let data = decode_fn(&self.function, &bytes)?;
// decode output
let data = decode_fn(&self.function, &bytes, false)?;

Ok(data)
}
Expand Down
13 changes: 5 additions & 8 deletions ethers-core/src/abi/human_readable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use thiserror::Error;
/// Parses a "human readable abi" string vector
///
/// ```
/// use ethers::contract::abi;
/// use ethers::abi::parse_abi;
///
/// let abi = abi::parse([
/// let abi = parse_abi(&[
/// "function x() external view returns (uint256)",
/// ]).unwrap()
/// ]).unwrap();
/// ```
pub fn parse(input: &[&str]) -> Result<Abi, ParseError> {
let mut abi = Abi {
Expand Down Expand Up @@ -48,7 +48,7 @@ pub fn parse(input: &[&str]) -> Result<Abi, ParseError> {
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];
let name = split[0].trim_end();
let rest = split[1];

let args = rest.replace(")", "");
Expand Down Expand Up @@ -103,7 +103,6 @@ fn parse_function(fn_string: &str) -> Result<Function, ParseError> {
// internal args
let args: Vec<&str> = split[1].split(")").collect();
let args: Vec<&str> = args[0].split(", ").collect();
dbg!(&args);
let inputs = args
.into_iter()
.filter(|x| !x.is_empty())
Expand All @@ -116,7 +115,6 @@ fn parse_function(fn_string: &str) -> Result<Function, ParseError> {
let ret = split[2].strip_suffix(")").expect("no right paren");
let ret: Vec<&str> = ret.split(", ").collect();

dbg!(&ret);
ret.into_iter()
// remove modifiers etc
.filter(|x| !x.is_empty())
Expand All @@ -137,7 +135,6 @@ fn parse_function(fn_string: &str) -> Result<Function, ParseError> {

// address x
fn parse_param(param: &str) -> Result<Param, ParseError> {
dbg!(&param);
let mut param = param
.split(" ")
.into_iter()
Expand Down Expand Up @@ -211,7 +208,7 @@ mod tests {
#[test]
fn parses_event() {
assert_eq!(
parse_event("event Foo(address indexed x, uint y, bytes32[] z)").unwrap(),
parse_event("event Foo (address indexed x, uint y, bytes32[] z)").unwrap(),
Event {
anonymous: false,
name: "Foo".to_owned(),
Expand Down

0 comments on commit 4fd2efe

Please sign in to comment.