Skip to content

Commit

Permalink
add tests for type extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
isum committed Nov 7, 2024
1 parent a6a1cbf commit 222cf87
Show file tree
Hide file tree
Showing 4 changed files with 398 additions and 5 deletions.
89 changes: 89 additions & 0 deletions graph/src/abi/event_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,92 @@ fn log_to_log_data(log: &Log) -> Result<LogData> {

LogData::new(topics, data).context("log has an invalid number of topics")
}

#[cfg(test)]
mod tests {
use alloy::dyn_abi::DynSolValue;
use alloy::primitives::U256;

use super::*;

fn make_log(topics: &[[u8; 32]], data: Vec<u8>) -> Log {
Log {
address: [1; 20].into(),
topics: topics.iter().map(Into::into).collect(),
data: data.into(),
block_hash: None,
block_number: None,
transaction_hash: None,
transaction_index: None,
log_index: None,
transaction_log_index: None,
log_type: None,
removed: None,
}
}

#[test]
fn decode_log_no_topic_0() {
let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap();
let a = U256::from(10).to_be_bytes::<32>();
let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode();

let log = make_log(&[a], b);
let err = event.decode_log(&log).unwrap_err();

assert_eq!(
err.to_string(),
"invalid log topic list length: expected 2 topics, got 1",
);
}

#[test]
fn decode_log_invalid_topic_0() {
let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap();
let a = U256::from(10).to_be_bytes::<32>();
let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode();

let log = make_log(&[[0; 32], a], b);
let err = event.decode_log(&log).unwrap_err();

assert!(err.to_string().starts_with("invalid event signature:"));
}

#[test]
fn decode_log_success() {
let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap();
let topic_0 = event.selector().0;
let a = U256::from(10).to_be_bytes::<32>();
let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode();

let log = make_log(&[topic_0, a], b);
let resp = event.decode_log(&log).unwrap();

assert_eq!(
resp,
vec![
DynSolParam {
name: "a".to_owned(),
value: DynSolValue::Uint(U256::from(10), 256),
},
DynSolParam {
name: "b".to_owned(),
value: DynSolValue::FixedBytes([10; 32].into(), 32),
}
],
);
}

#[test]
fn decode_log_too_many_topics() {
let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap();
let topic_0 = event.selector().0;
let a = U256::from(10).to_be_bytes::<32>();
let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode();

let log = make_log(&[topic_0, a, a, a, a], b);
let err = event.decode_log(&log).unwrap_err();

assert_eq!(err.to_string(), "log has an invalid number of topics");
}
}
141 changes: 141 additions & 0 deletions graph/src/abi/function_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub trait FunctionExt {
fn abi_decode_output(&self, data: &[u8]) -> Result<Vec<DynSolValue>>;

/// ABI-encodes the given values, prefixed by the function's selector, if any.
///
/// This behaviour is to ensure consistency with `ethabi`.
fn abi_encode_input(&self, values: &[DynSolValue]) -> Result<Vec<u8>>;
}

Expand Down Expand Up @@ -160,3 +162,142 @@ fn fix_type_size<'a>(ty: &DynSolType, val: &'a DynSolValue) -> Result<Cow<'a, Dy

Ok(Cow::Owned(new_val))
}

#[cfg(test)]
mod tests {
use alloy::primitives::I256;
use alloy::primitives::U256;

use super::*;

fn s(f: &str) -> String {
Function::parse(f).unwrap().signature_compat()
}

fn u256(u: u64) -> U256 {
U256::from(u)
}

fn i256(i: i32) -> I256 {
I256::try_from(i).unwrap()
}

#[test]
fn signature_compat_no_inputs_no_outputs() {
assert_eq!(s("x()"), "x()");
}

#[test]
fn signature_compat_one_input_no_outputs() {
assert_eq!(s("x(uint256 a)"), "x(uint256)");
}

#[test]
fn signature_compat_multiple_inputs_no_outputs() {
assert_eq!(s("x(uint256 a, bytes32 b)"), "x(uint256,bytes32)");
}

#[test]
fn signature_compat_no_inputs_one_output() {
assert_eq!(s("x() returns (uint256)"), "x():(uint256)");
}

#[test]
fn signature_compat_no_inputs_multiple_outputs() {
assert_eq!(s("x() returns (uint256, bytes32)"), "x():(uint256,bytes32)");
}

#[test]
fn signature_compat_multiple_inputs_multiple_outputs() {
assert_eq!(
s("x(bytes32 a, uint256 b) returns (uint256, bytes32)"),
"x(bytes32,uint256):(uint256,bytes32)",
);
}

#[test]
fn abi_decode_input() {
use DynSolValue::{Int, Tuple, Uint};

let f = Function::parse("x(uint256 a, int256 b)").unwrap();
let data = Tuple(vec![Uint(u256(10), 256), Int(i256(-10), 256)]).abi_encode_params();
let inputs = f.abi_decode_input(&data).unwrap();

assert_eq!(inputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]);
}

#[test]
fn abi_decode_output() {
use DynSolValue::{Int, Tuple, Uint};

let f = Function::parse("x() returns (uint256 a, int256 b)").unwrap();
let data = Tuple(vec![Uint(u256(10), 256), Int(i256(-10), 256)]).abi_encode_params();
let outputs = f.abi_decode_output(&data).unwrap();

assert_eq!(outputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]);
}

#[test]
fn abi_encode_input_no_values() {
let f = Function::parse("x(uint256 a, int256 b)").unwrap();
let err = f.abi_encode_input(&[]).unwrap_err();

assert_eq!(
err.to_string(),
"unexpected number of values; expected 2, got 0",
);
}

#[test]
fn abi_encode_input_too_many_values() {
use DynSolValue::Bool;

let f = Function::parse("x(uint256 a, int256 b)").unwrap();

let err = f
.abi_encode_input(&[Bool(true), Bool(false), Bool(true)])
.unwrap_err();

assert_eq!(
err.to_string(),
"unexpected number of values; expected 2, got 3",
);
}

#[test]
fn abi_encode_input_invalid_types() {
use DynSolValue::Bool;

let f = Function::parse("x(uint256 a, int256 b)").unwrap();
let err = f.abi_encode_input(&[Bool(true), Bool(false)]).unwrap_err();
assert!(err.to_string().starts_with("invalid value type;"));
}

#[test]
fn abi_encode_success() {
use DynSolValue::{Bool, Uint};

let f = Function::parse("x(uint256 a, bool b)").unwrap();
let a = Uint(u256(10), 256);
let b = Bool(true);

let data = f.abi_encode_input(&[a.clone(), b.clone()]).unwrap();
let inputs = f.abi_decode_input(&data[4..]).unwrap();

assert_eq!(inputs, vec![a, b]);
}

#[test]
fn abi_encode_success_with_size_fix() {
use DynSolValue::{Int, Uint};

let f = Function::parse("x(uint256 a, int256 b)").unwrap();
let a = Uint(u256(10), 32);
let b = Int(i256(-10), 32);

let data = f.abi_encode_input(&[a, b]).unwrap();
let inputs = f.abi_decode_input(&data[4..]).unwrap();

assert_eq!(inputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]);
}
}
2 changes: 1 addition & 1 deletion graph/src/abi/param.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use alloy::dyn_abi::DynSolValue;

#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct DynSolParam {
pub name: String,
pub value: DynSolValue,
Expand Down
Loading

0 comments on commit 222cf87

Please sign in to comment.