Skip to content

Commit 7a9b399

Browse files
committed
add tests for type extensions
1 parent 05532f0 commit 7a9b399

File tree

4 files changed

+398
-5
lines changed

4 files changed

+398
-5
lines changed

graph/src/abi/event_ext.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,92 @@ fn log_to_log_data(log: &Log) -> Result<LogData> {
5151

5252
LogData::new(topics, data).context("log has an invalid number of topics")
5353
}
54+
55+
#[cfg(test)]
56+
mod tests {
57+
use alloy::dyn_abi::DynSolValue;
58+
use alloy::primitives::U256;
59+
60+
use super::*;
61+
62+
fn make_log(topics: &[[u8; 32]], data: Vec<u8>) -> Log {
63+
Log {
64+
address: [1; 20].into(),
65+
topics: topics.iter().map(Into::into).collect(),
66+
data: data.into(),
67+
block_hash: None,
68+
block_number: None,
69+
transaction_hash: None,
70+
transaction_index: None,
71+
log_index: None,
72+
transaction_log_index: None,
73+
log_type: None,
74+
removed: None,
75+
}
76+
}
77+
78+
#[test]
79+
fn decode_log_no_topic_0() {
80+
let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap();
81+
let a = U256::from(10).to_be_bytes::<32>();
82+
let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode();
83+
84+
let log = make_log(&[a], b);
85+
let err = event.decode_log(&log).unwrap_err();
86+
87+
assert_eq!(
88+
err.to_string(),
89+
"invalid log topic list length: expected 2 topics, got 1",
90+
);
91+
}
92+
93+
#[test]
94+
fn decode_log_invalid_topic_0() {
95+
let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap();
96+
let a = U256::from(10).to_be_bytes::<32>();
97+
let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode();
98+
99+
let log = make_log(&[[0; 32], a], b);
100+
let err = event.decode_log(&log).unwrap_err();
101+
102+
assert!(err.to_string().starts_with("invalid event signature:"));
103+
}
104+
105+
#[test]
106+
fn decode_log_success() {
107+
let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap();
108+
let topic_0 = event.selector().0;
109+
let a = U256::from(10).to_be_bytes::<32>();
110+
let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode();
111+
112+
let log = make_log(&[topic_0, a], b);
113+
let resp = event.decode_log(&log).unwrap();
114+
115+
assert_eq!(
116+
resp,
117+
vec![
118+
DynSolParam {
119+
name: "a".to_owned(),
120+
value: DynSolValue::Uint(U256::from(10), 256),
121+
},
122+
DynSolParam {
123+
name: "b".to_owned(),
124+
value: DynSolValue::FixedBytes([10; 32].into(), 32),
125+
}
126+
],
127+
);
128+
}
129+
130+
#[test]
131+
fn decode_log_too_many_topics() {
132+
let event = Event::parse("event X(uint256 indexed a, bytes32 b)").unwrap();
133+
let topic_0 = event.selector().0;
134+
let a = U256::from(10).to_be_bytes::<32>();
135+
let b = DynSolValue::FixedBytes([10; 32].into(), 32).abi_encode();
136+
137+
let log = make_log(&[topic_0, a, a, a, a], b);
138+
let err = event.decode_log(&log).unwrap_err();
139+
140+
assert_eq!(err.to_string(), "log has an invalid number of topics");
141+
}
142+
}

graph/src/abi/function_ext.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub trait FunctionExt {
3030
fn abi_decode_output(&self, data: &[u8]) -> Result<Vec<DynSolValue>>;
3131

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

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

161163
Ok(Cow::Owned(new_val))
162164
}
165+
166+
#[cfg(test)]
167+
mod tests {
168+
use alloy::primitives::I256;
169+
use alloy::primitives::U256;
170+
171+
use super::*;
172+
173+
fn s(f: &str) -> String {
174+
Function::parse(f).unwrap().signature_compat()
175+
}
176+
177+
fn u256(u: u64) -> U256 {
178+
U256::from(u)
179+
}
180+
181+
fn i256(i: i32) -> I256 {
182+
I256::try_from(i).unwrap()
183+
}
184+
185+
#[test]
186+
fn signature_compat_no_inputs_no_outputs() {
187+
assert_eq!(s("x()"), "x()");
188+
}
189+
190+
#[test]
191+
fn signature_compat_one_input_no_outputs() {
192+
assert_eq!(s("x(uint256 a)"), "x(uint256)");
193+
}
194+
195+
#[test]
196+
fn signature_compat_multiple_inputs_no_outputs() {
197+
assert_eq!(s("x(uint256 a, bytes32 b)"), "x(uint256,bytes32)");
198+
}
199+
200+
#[test]
201+
fn signature_compat_no_inputs_one_output() {
202+
assert_eq!(s("x() returns (uint256)"), "x():(uint256)");
203+
}
204+
205+
#[test]
206+
fn signature_compat_no_inputs_multiple_outputs() {
207+
assert_eq!(s("x() returns (uint256, bytes32)"), "x():(uint256,bytes32)");
208+
}
209+
210+
#[test]
211+
fn signature_compat_multiple_inputs_multiple_outputs() {
212+
assert_eq!(
213+
s("x(bytes32 a, uint256 b) returns (uint256, bytes32)"),
214+
"x(bytes32,uint256):(uint256,bytes32)",
215+
);
216+
}
217+
218+
#[test]
219+
fn abi_decode_input() {
220+
use DynSolValue::{Int, Tuple, Uint};
221+
222+
let f = Function::parse("x(uint256 a, int256 b)").unwrap();
223+
let data = Tuple(vec![Uint(u256(10), 256), Int(i256(-10), 256)]).abi_encode_params();
224+
let inputs = f.abi_decode_input(&data).unwrap();
225+
226+
assert_eq!(inputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]);
227+
}
228+
229+
#[test]
230+
fn abi_decode_output() {
231+
use DynSolValue::{Int, Tuple, Uint};
232+
233+
let f = Function::parse("x() returns (uint256 a, int256 b)").unwrap();
234+
let data = Tuple(vec![Uint(u256(10), 256), Int(i256(-10), 256)]).abi_encode_params();
235+
let outputs = f.abi_decode_output(&data).unwrap();
236+
237+
assert_eq!(outputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]);
238+
}
239+
240+
#[test]
241+
fn abi_encode_input_no_values() {
242+
let f = Function::parse("x(uint256 a, int256 b)").unwrap();
243+
let err = f.abi_encode_input(&[]).unwrap_err();
244+
245+
assert_eq!(
246+
err.to_string(),
247+
"unexpected number of values; expected 2, got 0",
248+
);
249+
}
250+
251+
#[test]
252+
fn abi_encode_input_too_many_values() {
253+
use DynSolValue::Bool;
254+
255+
let f = Function::parse("x(uint256 a, int256 b)").unwrap();
256+
257+
let err = f
258+
.abi_encode_input(&[Bool(true), Bool(false), Bool(true)])
259+
.unwrap_err();
260+
261+
assert_eq!(
262+
err.to_string(),
263+
"unexpected number of values; expected 2, got 3",
264+
);
265+
}
266+
267+
#[test]
268+
fn abi_encode_input_invalid_types() {
269+
use DynSolValue::Bool;
270+
271+
let f = Function::parse("x(uint256 a, int256 b)").unwrap();
272+
let err = f.abi_encode_input(&[Bool(true), Bool(false)]).unwrap_err();
273+
assert!(err.to_string().starts_with("invalid value type;"));
274+
}
275+
276+
#[test]
277+
fn abi_encode_success() {
278+
use DynSolValue::{Bool, Uint};
279+
280+
let f = Function::parse("x(uint256 a, bool b)").unwrap();
281+
let a = Uint(u256(10), 256);
282+
let b = Bool(true);
283+
284+
let data = f.abi_encode_input(&[a.clone(), b.clone()]).unwrap();
285+
let inputs = f.abi_decode_input(&data[4..]).unwrap();
286+
287+
assert_eq!(inputs, vec![a, b]);
288+
}
289+
290+
#[test]
291+
fn abi_encode_success_with_size_fix() {
292+
use DynSolValue::{Int, Uint};
293+
294+
let f = Function::parse("x(uint256 a, int256 b)").unwrap();
295+
let a = Uint(u256(10), 32);
296+
let b = Int(i256(-10), 32);
297+
298+
let data = f.abi_encode_input(&[a, b]).unwrap();
299+
let inputs = f.abi_decode_input(&data[4..]).unwrap();
300+
301+
assert_eq!(inputs, vec![Uint(u256(10), 256), Int(i256(-10), 256)]);
302+
}
303+
}

graph/src/abi/param.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloy::dyn_abi::DynSolValue;
22

3-
#[derive(Clone, Debug)]
3+
#[derive(Clone, Debug, PartialEq)]
44
pub struct DynSolParam {
55
pub name: String,
66
pub value: DynSolValue,

0 commit comments

Comments
 (0)