1use alloy_dyn_abi::{DynSolEvent, DynSolType, DynSolValue, Specifier};
5use alloy_json_abi::{ContractObject, Function, JsonAbi, StateMutability};
6use alloy_primitives::{Bytes, Log, LogData};
7use anyhow::{anyhow, bail, Result};
8use std::collections::BTreeMap;
9
10type EventMap = BTreeMap<std::string::String, Vec<alloy_json_abi::Event>>;
11
12#[derive(Debug)]
19pub struct EventLog {
20 pub name: String,
22 pub decoder: DynSolEvent,
24}
25
26impl EventLog {
27 pub fn decode(&self, log: &LogData) -> Option<(String, DynSolValue)> {
30 if let Ok(r) = self.decoder.decode_log(log, true) {
31 let v = DynSolValue::Tuple([r.indexed, r.body].concat());
32 return Some((self.name.clone(), v));
33 }
34 None
35 }
36}
37
38pub struct ContractAbi {
39 pub abi: JsonAbi,
41 pub bytecode: Option<Bytes>,
43 pub events_logs: Vec<EventLog>,
45}
46
47fn convert_events(ev: &EventMap) -> Vec<EventLog> {
50 ev.iter()
51 .flat_map(|(k, v)| {
52 v.iter()
53 .map(|e| EventLog {
54 name: k.clone(),
55 decoder: e.resolve().unwrap(),
56 })
57 .collect::<Vec<EventLog>>()
58 })
59 .collect::<Vec<EventLog>>()
60}
61
62impl ContractAbi {
63 pub fn from_full_json(raw: &str) -> Self {
66 let co =
67 serde_json::from_str::<ContractObject>(raw).expect("Abi: failed to parse abi to json");
68 if co.abi.is_none() {
69 panic!("Abi: ABI not found in file")
70 }
71 if co.bytecode.is_none() {
72 panic!("Abi: Bytecode not found in file")
73 }
74 let abi = co.abi.unwrap();
75 let evts = convert_events(&abi.events);
76 Self {
77 abi,
78 bytecode: co.bytecode,
79 events_logs: evts,
80 }
81 }
82
83 pub fn from_abi_bytecode(raw: &str, bytecode: Option<Vec<u8>>) -> Self {
86 let abi = serde_json::from_str::<JsonAbi>(raw).expect("Abi: failed to parse abi");
87 let evts = convert_events(&abi.events);
88 Self {
89 abi,
90 bytecode: bytecode.map(Bytes::from),
91 events_logs: evts,
92 }
93 }
94
95 pub fn from_human_readable(input: Vec<&str>) -> Self {
98 let abi = JsonAbi::parse(input).expect("Abi: Invalid solidity function(s) format");
99 let evts = convert_events(&abi.events);
100 Self {
101 abi,
102 bytecode: None,
103 events_logs: evts,
104 }
105 }
106
107 pub fn extract_logs(&self, logs: Vec<Log>) -> Vec<(String, DynSolValue)> {
109 let mut results: Vec<(String, DynSolValue)> = Vec::new();
110 for log in logs {
111 for e in &self.events_logs {
112 if let Some(p) = e.decode(&log.data) {
113 results.push(p);
114 }
115 }
116 }
117 results
118 }
119
120 pub fn has_function(&self, name: &str) -> bool {
122 self.abi.functions.contains_key(name)
123 }
124
125 pub fn has_fallback(&self) -> bool {
127 self.abi.fallback.is_some()
128 }
129
130 pub fn has_receive(&self) -> bool {
132 self.abi.receive.is_some()
133 }
134
135 pub fn bytecode(&self) -> Option<Vec<u8>> {
137 self.bytecode.as_ref().map(|b| b.to_vec())
138 }
139
140 pub fn encode_constructor(&self, args: &str) -> Result<(Vec<u8>, bool)> {
145 let bytecode = match self.bytecode() {
146 Some(b) => b,
147 _ => bail!("Abi: Missing contract bytecode!"),
148 };
149
150 let constructor = match &self.abi.constructor {
151 Some(c) => c,
152 _ => return Ok((bytecode, false)),
153 };
154
155 let types = constructor
156 .inputs
157 .iter()
158 .map(|i| i.resolve().unwrap())
159 .collect::<Vec<_>>();
160
161 let ty = DynSolType::Tuple(types);
162 let dynavalues = ty.coerce_str(args).map_err(|_| {
163 anyhow!("Abi: Error coercing the arguments for the constructor. Check the input argument(s)")
164 })?;
165 let encoded_args = dynavalues.abi_encode_params();
166 let is_payable = matches!(constructor.state_mutability, StateMutability::Payable);
167
168 Ok(([bytecode, encoded_args].concat(), is_payable))
169 }
170
171 fn extract(funcs: &Function, args: &str) -> Result<DynSolValue> {
172 let types = funcs
173 .inputs
174 .iter()
175 .map(|i| i.resolve().unwrap())
176 .collect::<Vec<_>>();
177 let ty = DynSolType::Tuple(types);
178 ty.coerce_str(args).map_err(|_| {
179 anyhow!(
180 "Abi: Error coercing the arguments for the function call. Check the input argument(s)"
181 )
182 })
183 }
184
185 pub fn encode_function(
203 &self,
204 name: &str,
205 args: &str,
206 ) -> anyhow::Result<(Vec<u8>, bool, Option<DynSolType>)> {
207 let funcs = match self.abi.function(name) {
208 Some(funcs) => funcs,
209 _ => bail!("Abi: Function {} not found in the ABI!", name),
210 };
211
212 for f in funcs {
214 let result = Self::extract(f, args);
215 let is_payable = matches!(f.state_mutability, StateMutability::Payable);
216 if result.is_ok() {
217 let ty = match f.outputs.len() {
219 0 => None,
220 1 => f.outputs.first().unwrap().clone().resolve().ok(),
221 _ => {
222 let t = f
223 .outputs
224 .iter()
225 .map(|i| i.resolve().unwrap())
226 .collect::<Vec<_>>();
227 Some(DynSolType::Tuple(t))
228 }
229 };
230
231 let selector = f.selector().to_vec();
232 let encoded_args = result.unwrap().abi_encode_params();
233 let all = [selector, encoded_args].concat();
234
235 return Ok((all, is_payable, ty));
236 }
237 }
238
239 Err(anyhow::anyhow!(
242 "Abi: Arguments to the function do not match what is expected"
243 ))
244 }
245}
246
247#[cfg(test)]
248mod tests {
249
250 use super::*;
251 use alloy_primitives::{b256, bytes, Address, FixedBytes, LogData, U256};
252 use alloy_sol_types::{sol, SolCall};
253 use hex::FromHex;
254
255 sol! {
256
257 struct HelloInput {
258 uint256 value;
259 address owner;
260 uint160 beta;
261 }
262
263 contract HelloWorld {
264 address public owner;
265 function hello(HelloInput params) external returns (bool);
266 }
267 }
268
269 sol! {
270 contract MrOverLoads {
271 function one() public returns (bool);
272 function one(uint256);
273 function one(address, (uint64, uint64)) public returns (address);
274 }
275 }
276
277 sol! {
278 struct A {
279 uint256 value;
280 address owner;
281 bool isok;
282 }
283
284 struct B {
285 bytes data;
286 }
287
288 contract KitchenSink {
289 function check_types(uint256, bool, address, string, bytes32);
290 function check_both(A, B);
291 function check_blend(string, uint160, A);
292 }
293 }
294
295 #[test]
296 fn check_constructor_encoding() {
297 let input = vec!["constructor()"];
298 let mut abi = ContractAbi::from_human_readable(input);
299 abi.bytecode = Some(b"hello".into());
301
302 assert!(abi.encode_constructor("()").is_ok());
303 assert!(abi.encode_constructor("(1234)").is_err());
304 }
305
306 #[test]
307 fn encoding_function_decoder_types() {
308 let tc = ContractAbi::from_human_readable(vec![
309 "function a()",
310 "function b() (uint256)",
311 "function c() (bool, address, uint256)",
312 ]);
313
314 let (_, _, r1) = tc.encode_function("a", "()").unwrap();
315 let (_, _, r2) = tc.encode_function("b", "()").unwrap();
316 let (_, _, r3) = tc.encode_function("c", "()").unwrap();
317
318 assert_eq!(None, r1);
319 assert_eq!(Some(DynSolType::Uint(256)), r2);
320 assert_eq!(
321 Some(DynSolType::Tuple(vec![
322 DynSolType::Bool,
323 DynSolType::Address,
324 DynSolType::Uint(256)
325 ])),
326 r3
327 );
328 }
329
330 #[test]
331 fn encoding_functions() {
332 let hello_world = vec!["function hello(tuple(uint256, address, uint160)) (bool)"];
333 let hw = ContractAbi::from_human_readable(hello_world);
334 assert!(hw.has_function("hello"));
335
336 let addy = Address::with_last_byte(24);
337 let solencoded = HelloWorld::helloCall {
338 params: HelloInput {
339 value: U256::from(10),
340 owner: addy,
341 beta: U256::from(1),
342 },
343 }
344 .abi_encode();
345
346 assert!(hw.encode_function("bob", "()").is_err());
347 assert!(hw.encode_function("hello", "(1,2").is_err());
348
349 let (cencoded, is_payable, dtype) = hw
350 .encode_function("hello", &format!("(({}, {}, {}))", 10, addy.to_string(), 1))
351 .unwrap();
352
353 assert!(!is_payable);
354 assert_eq!(solencoded, cencoded);
355 assert_eq!(dtype, Some(DynSolType::Bool));
356 }
357
358 #[test]
359 fn encoding_overloaded_functions() {
360 let overit = vec![
361 "function one() (bool)",
362 "function one(uint256)",
363 "function one(address, (uint64, uint64)) (address)",
364 ];
365 let abi = ContractAbi::from_human_readable(overit);
366 let addy = Address::with_last_byte(24);
367
368 let sa = MrOverLoads::one_0Call {}.abi_encode();
369 let (aa, _, _) = abi.encode_function("one", "()").unwrap();
370 assert_eq!(sa, aa);
371
372 let sb = MrOverLoads::one_1Call { _0: U256::from(1) }.abi_encode();
373 let (ab, _, _) = abi.encode_function("one", "(1)").unwrap();
374 assert_eq!(sb, ab);
375
376 let sc = MrOverLoads::one_2Call {
377 _0: addy,
378 _1: (10u64, 11u64),
379 }
380 .abi_encode();
381 let (ac, _, otype) = abi
382 .encode_function("one", &format!("({},({},{}))", addy.to_string(), 10, 11))
383 .unwrap();
384
385 assert_eq!(sc, ac);
386 assert_eq!(Some(DynSolType::Address), otype);
387 }
388
389 #[test]
390 fn encode_kitchen_sink() {
391 let addy = "0x023e09e337f5a6c82e62fe5ae4b6396d34930751";
392 let expected_check_types = KitchenSink::check_typesCall {
397 _0: U256::from(1),
398 _1: true,
399 _2: Address::from_hex(addy).unwrap(),
400 _3: "bob".to_string(),
401 _4: FixedBytes::from_slice(&[1u8; 32]),
402 }
403 .abi_encode();
404
405 let abi = ContractAbi::from_human_readable(vec![
406 "function check_types(uint256, bool, address, string, bytes32)",
407 "function check_both(tuple(uint256, address, bool), tuple(bytes))",
408 "function check_blend(string, uint160, tuple(uint256, address, bool))",
409 ]);
410
411 let input = "(1, true, 0x023e09e337f5a6c82e62fe5ae4b6396d34930751, 'bob', 0101010101010101010101010101010101010101010101010101010101010101)";
413 let (actual, _, _) = abi.encode_function("check_types", input).unwrap();
414 assert_eq!(expected_check_types, actual);
415
416 let expected_check_both = KitchenSink::check_bothCall {
417 _0: A {
418 value: U256::from(10),
419 owner: Address::from_hex(addy).unwrap(),
420 isok: false,
421 },
422 _1: B { data: Bytes::new() },
423 }
424 .abi_encode();
425
426 let input_both = "((10, 0x023e09e337f5a6c82e62fe5ae4b6396d34930751, false),(0x))";
427 let (actualboth, _, _) = abi.encode_function("check_both", input_both).unwrap();
428 assert_eq!(expected_check_both, actualboth);
429
430 let expected_check_blend = KitchenSink::check_blendCall {
431 _0: "bob".to_string(),
432 _1: U256::from(5),
433 _2: A {
434 value: U256::from(10),
435 owner: Address::from_hex(addy).unwrap(),
436 isok: false,
437 },
438 }
439 .abi_encode();
440
441 let input_blend = "(bob, 5,(10, 0x023e09e337f5a6c82e62fe5ae4b6396d34930751, false))";
442 let (actualblend, _, _) = abi.encode_function("check_blend", input_blend).unwrap();
443 assert_eq!(expected_check_blend, actualblend)
444 }
445
446 #[test]
447 fn test_flatten_event_structure() {
448 let sample = ContractAbi::from_human_readable(vec![
451 "event Transfer(address indexed from,address indexed to,uint256 amount)",
452 "event Transfer(address indexed from) anonymous",
453 "event Mint(address indexed recip,uint256 amount)",
454 "event Burn(address indexed recip,uint256 amount)",
455 ]);
456
457 assert_eq!(4, sample.events_logs.len());
458
459 let transfer = LogData::new_unchecked(
460 vec![
461 b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
462 b256!("000000000000000000000000c2e9f25be6257c210d7adf0d4cd6e3e881ba25f8"),
463 b256!("0000000000000000000000002b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"),
464 ],
465 bytes!("0000000000000000000000000000000000000000000000000000000000000005"),
466 );
467
468 let burn = LogData::new_unchecked(
469 vec![
470 b256!("cc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5"),
471 b256!("000000000000000000000000c2e9f25be6257c210d7adf0d4cd6e3e881ba25f8"),
472 ],
473 bytes!("0000000000000000000000000000000000000000000000000000000000000005"),
474 );
475
476 let log_address = Address::repeat_byte(14);
477 let logs = vec![
478 Log {
479 address: log_address,
480 data: transfer,
481 },
482 Log {
483 address: log_address,
484 data: burn,
485 },
486 ];
487
488 let results = sample.extract_logs(logs);
489 assert_eq!(2, results.len());
490
491 }
493}