1use tiny_keccak::{Hasher, Keccak};
5
6use crate::error::DecodeError;
7
8#[derive(Debug, Clone)]
10pub struct FunctionSignature {
11 pub name: String,
12 pub params: Vec<ParamType>,
13 pub param_names: Vec<Option<String>>,
14 pub canonical: String,
15 pub selector: [u8; 4],
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum ParamType {
21 Address,
22 Uint(usize),
23 Int(usize),
24 Bool,
25 Bytes,
26 FixedBytes(usize),
27 String,
28 Array(Box<ParamType>),
29 FixedArray(Box<ParamType>, usize),
30 Tuple(Vec<(Option<String>, ParamType)>),
31}
32
33impl ParamType {
34 pub fn is_dynamic(&self) -> bool {
36 match self {
37 ParamType::Bytes | ParamType::String => true,
38 ParamType::Array(_) => true,
39 ParamType::FixedArray(inner, _) => inner.is_dynamic(),
40 ParamType::Tuple(members) => members.iter().any(|(_, m)| m.is_dynamic()),
41 _ => false,
42 }
43 }
44
45 pub fn head_size(&self) -> usize {
51 if self.is_dynamic() {
52 32 } else {
54 match self {
55 ParamType::Tuple(members) => members.iter().map(|(_, m)| m.head_size()).sum(),
56 ParamType::FixedArray(inner, len) => inner.head_size() * len,
57 _ => 32, }
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct DecodedArguments {
66 pub function_name: String,
67 pub selector: [u8; 4],
68 pub args: Vec<DecodedArgument>,
69}
70
71#[derive(Debug, Clone)]
73pub struct DecodedArgument {
74 pub index: usize,
75 pub name: Option<String>,
76 pub param_type: ParamType,
77 pub value: ArgumentValue,
78}
79
80#[derive(Debug, Clone)]
82pub enum ArgumentValue {
83 Address([u8; 20]),
84 Uint(Vec<u8>),
85 Int(Vec<u8>),
86 Bool(bool),
87 Bytes(Vec<u8>),
88 FixedBytes(Vec<u8>),
89 String(std::string::String),
90 Array(Vec<ArgumentValue>),
91 Tuple(Vec<(Option<String>, ArgumentValue)>),
92}
93
94impl ArgumentValue {
95 pub fn to_json_value(&self) -> serde_json::Value {
97 match self {
98 ArgumentValue::Address(addr) => {
99 serde_json::Value::String(format!("0x{}", hex::encode(addr)))
100 }
101 ArgumentValue::Uint(bytes) => {
102 let hex_str = format!("0x{}", hex::encode(bytes));
103 serde_json::Value::String(hex_str)
104 }
105 ArgumentValue::Int(bytes) => {
106 let hex_str = format!("0x{}", hex::encode(bytes));
107 serde_json::Value::String(hex_str)
108 }
109 ArgumentValue::Bool(b) => serde_json::Value::Bool(*b),
110 ArgumentValue::Bytes(b) => serde_json::Value::String(format!("0x{}", hex::encode(b))),
111 ArgumentValue::FixedBytes(b) => {
112 serde_json::Value::String(format!("0x{}", hex::encode(b)))
113 }
114 ArgumentValue::String(s) => serde_json::Value::String(s.clone()),
115 ArgumentValue::Array(items) => {
116 serde_json::Value::Array(items.iter().map(|i| i.to_json_value()).collect())
117 }
118 ArgumentValue::Tuple(items) => {
119 serde_json::Value::Array(items.iter().map(|(_, i)| i.to_json_value()).collect())
120 }
121 }
122 }
123
124 pub fn as_uint_bytes(&self) -> Option<[u8; 32]> {
126 match self {
127 ArgumentValue::Uint(b) | ArgumentValue::Int(b) => {
128 let mut result = [0u8; 32];
129 let start = 32usize.saturating_sub(b.len());
130 let copy_len = b.len().min(32);
131 result[start..start + copy_len].copy_from_slice(&b[b.len() - copy_len..]);
132 Some(result)
133 }
134 _ => None,
135 }
136 }
137}
138
139pub fn parse_signature(sig: &str) -> Result<FunctionSignature, DecodeError> {
143 let sig = sig.trim();
144 let open = sig
145 .find('(')
146 .ok_or_else(|| DecodeError::InvalidSignature(format!("missing '(' in: {sig}")))?;
147
148 if !sig.ends_with(')') {
149 return Err(DecodeError::InvalidSignature(format!(
150 "missing ')' in: {sig}"
151 )));
152 }
153
154 let name = sig[..open].to_string();
155 if name.is_empty() {
156 return Err(DecodeError::InvalidSignature(
157 "empty function name".to_string(),
158 ));
159 }
160
161 let params_str = &sig[open + 1..sig.len() - 1];
162 let (params, param_names) = if params_str.is_empty() {
163 (vec![], vec![])
164 } else {
165 let named = parse_param_list_named(params_str)?;
166 let params = named.iter().map(|(p, _)| p.clone()).collect();
167 let names = named.into_iter().map(|(_, n)| n).collect();
168 (params, names)
169 };
170
171 let canonical = format!("{}({})", name, canonical_params(¶ms));
172 let selector = selector_from_signature(&canonical);
173
174 Ok(FunctionSignature {
175 name,
176 params,
177 param_names,
178 canonical,
179 selector,
180 })
181}
182
183fn parse_param_list_named(s: &str) -> Result<Vec<(ParamType, Option<String>)>, DecodeError> {
185 let mut result = Vec::new();
186 let mut depth = 0usize;
187 let mut start = 0;
188
189 for (i, c) in s.char_indices() {
190 match c {
191 '(' => depth += 1,
192 ')' => {
193 depth = depth
194 .checked_sub(1)
195 .ok_or_else(|| DecodeError::InvalidSignature("unbalanced ')'".to_string()))?;
196 }
197 ',' if depth == 0 => {
198 result.push(parse_param_with_name(s[start..i].trim())?);
199 start = i + 1;
200 }
201 _ => {}
202 }
203 }
204
205 if depth != 0 {
206 return Err(DecodeError::InvalidSignature(
207 "unbalanced parentheses".to_string(),
208 ));
209 }
210
211 let last = s[start..].trim();
212 if !last.is_empty() {
213 result.push(parse_param_with_name(last)?);
214 }
215
216 Ok(result)
217}
218
219fn parse_param_list_with_names(s: &str) -> Result<Vec<(Option<String>, ParamType)>, DecodeError> {
223 let mut result = Vec::new();
224 let mut depth = 0usize;
225 let mut start = 0;
226
227 for (i, c) in s.char_indices() {
228 match c {
229 '(' => depth += 1,
230 ')' => {
231 depth = depth
232 .checked_sub(1)
233 .ok_or_else(|| DecodeError::InvalidSignature("unbalanced ')'".to_string()))?;
234 }
235 ',' if depth == 0 => {
236 let (pt, name) = parse_param_with_name(s[start..i].trim())?;
237 result.push((name, pt));
238 start = i + 1;
239 }
240 _ => {}
241 }
242 }
243
244 if depth != 0 {
245 return Err(DecodeError::InvalidSignature(
246 "unbalanced parentheses".to_string(),
247 ));
248 }
249
250 let last = s[start..].trim();
251 if !last.is_empty() {
252 let (pt, name) = parse_param_with_name(last)?;
253 result.push((name, pt));
254 }
255
256 Ok(result)
257}
258
259fn parse_param_with_name(s: &str) -> Result<(ParamType, Option<String>), DecodeError> {
261 let s = s.trim();
262
263 if let Some(pos) = s.rfind([')', ']']) {
265 let after = s[pos + 1..].trim();
266 if after.is_empty() {
267 return Ok((parse_param_type(s)?, None));
268 }
269 let type_str = &s[..pos + 1];
270 return Ok((parse_param_type(type_str)?, Some(after.to_string())));
271 }
272
273 if let Some(space_pos) = s.find(' ') {
275 let type_str = &s[..space_pos];
276 let name = s[space_pos..].trim();
277 return Ok((parse_param_type(type_str)?, Some(name.to_string())));
278 }
279
280 Ok((parse_param_type(s)?, None))
282}
283
284fn parse_param_type(s: &str) -> Result<ParamType, DecodeError> {
286 let s = s.trim();
287
288 if let Some(bracket_pos) = s.rfind('[') {
290 if s.ends_with(']') {
291 let inner_str = &s[..bracket_pos];
292 let size_str = &s[bracket_pos + 1..s.len() - 1];
293 let inner = parse_param_type(inner_str)?;
294
295 if size_str.is_empty() {
296 return Ok(ParamType::Array(Box::new(inner)));
297 } else {
298 let size: usize = size_str.parse().map_err(|_| {
299 DecodeError::InvalidSignature(format!("invalid array size: {size_str}"))
300 })?;
301 return Ok(ParamType::FixedArray(Box::new(inner), size));
302 }
303 }
304 }
305
306 if s.starts_with('(') && s.ends_with(')') {
308 let inner = &s[1..s.len() - 1];
309 let members = if inner.is_empty() {
310 vec![]
311 } else {
312 parse_param_list_with_names(inner)?
313 };
314 return Ok(ParamType::Tuple(members));
315 }
316
317 match s {
319 "address" => Ok(ParamType::Address),
320 "bool" => Ok(ParamType::Bool),
321 "string" => Ok(ParamType::String),
322 "bytes" => Ok(ParamType::Bytes),
323 _ if s.starts_with("uint") => {
324 let bits = if s == "uint" {
325 256
326 } else {
327 s[4..].parse::<usize>().map_err(|_| {
328 DecodeError::InvalidSignature(format!("invalid uint width: {s}"))
329 })?
330 };
331 Ok(ParamType::Uint(bits))
332 }
333 _ if s.starts_with("int") => {
334 let bits = if s == "int" {
335 256
336 } else {
337 s[3..]
338 .parse::<usize>()
339 .map_err(|_| DecodeError::InvalidSignature(format!("invalid int width: {s}")))?
340 };
341 Ok(ParamType::Int(bits))
342 }
343 _ if s.starts_with("bytes") => {
344 let size: usize = s[5..]
345 .parse()
346 .map_err(|_| DecodeError::InvalidSignature(format!("invalid bytes width: {s}")))?;
347 Ok(ParamType::FixedBytes(size))
348 }
349 _ => Err(DecodeError::InvalidSignature(format!("unknown type: {s}"))),
350 }
351}
352
353fn canonical_params(params: &[ParamType]) -> String {
355 params
356 .iter()
357 .map(canonical_param)
358 .collect::<Vec<_>>()
359 .join(",")
360}
361
362fn canonical_param(p: &ParamType) -> String {
363 match p {
364 ParamType::Address => "address".to_string(),
365 ParamType::Uint(bits) => format!("uint{bits}"),
366 ParamType::Int(bits) => format!("int{bits}"),
367 ParamType::Bool => "bool".to_string(),
368 ParamType::Bytes => "bytes".to_string(),
369 ParamType::FixedBytes(size) => format!("bytes{size}"),
370 ParamType::String => "string".to_string(),
371 ParamType::Array(inner) => format!("{}[]", canonical_param(inner)),
372 ParamType::FixedArray(inner, size) => format!("{}[{size}]", canonical_param(inner)),
373 ParamType::Tuple(members) => {
374 let inner = members
375 .iter()
376 .map(|(_, p)| canonical_param(p))
377 .collect::<Vec<_>>()
378 .join(",");
379 format!("({inner})")
380 }
381 }
382}
383
384fn selector_from_signature(canonical: &str) -> [u8; 4] {
386 let mut hasher = Keccak::v256();
387 hasher.update(canonical.as_bytes());
388 let mut hash = [0u8; 32];
389 hasher.finalize(&mut hash);
390 [hash[0], hash[1], hash[2], hash[3]]
391}
392
393pub fn decode_calldata(
395 sig: &FunctionSignature,
396 calldata: &[u8],
397) -> Result<DecodedArguments, DecodeError> {
398 if calldata.len() < 4 {
399 return Err(DecodeError::CalldataTooShort {
400 expected: 4,
401 actual: calldata.len(),
402 });
403 }
404
405 let actual_selector = &calldata[..4];
406 if actual_selector != sig.selector {
407 return Err(DecodeError::SelectorMismatch {
408 expected: hex::encode(sig.selector),
409 actual: hex::encode(actual_selector),
410 });
411 }
412
413 let data = &calldata[4..];
414 let mut args = Vec::with_capacity(sig.params.len());
415
416 let mut offset = 0;
418 for (i, param) in sig.params.iter().enumerate() {
419 let value = decode_value(param, data, offset, 0)?;
420 args.push(DecodedArgument {
421 index: i,
422 name: sig.param_names.get(i).cloned().flatten(),
423 param_type: param.clone(),
424 value,
425 });
426 offset += param.head_size();
427 }
428
429 Ok(DecodedArguments {
430 function_name: sig.name.clone(),
431 selector: sig.selector,
432 args,
433 })
434}
435
436fn decode_value(
443 param: &ParamType,
444 data: &[u8],
445 head_offset: usize,
446 base_offset: usize,
447) -> Result<ArgumentValue, DecodeError> {
448 if param.is_dynamic() {
449 let relative_offset = read_u256_as_usize(data, head_offset)?;
451 let absolute_offset = base_offset + relative_offset;
452 decode_value_at(param, data, absolute_offset)
453 } else {
454 decode_value_at(param, data, head_offset)
455 }
456}
457
458fn decode_value_at(
460 param: &ParamType,
461 data: &[u8],
462 offset: usize,
463) -> Result<ArgumentValue, DecodeError> {
464 ensure_bytes(data, offset, 32)?;
465
466 match param {
467 ParamType::Address => {
468 let word = &data[offset..offset + 32];
469 let mut addr = [0u8; 20];
470 addr.copy_from_slice(&word[12..32]);
471 Ok(ArgumentValue::Address(addr))
472 }
473 ParamType::Uint(_) | ParamType::Int(_) => {
474 let word = data[offset..offset + 32].to_vec();
475 if matches!(param, ParamType::Uint(_)) {
476 Ok(ArgumentValue::Uint(word))
477 } else {
478 Ok(ArgumentValue::Int(word))
479 }
480 }
481 ParamType::Bool => {
482 let b = data[offset + 31] != 0;
483 Ok(ArgumentValue::Bool(b))
484 }
485 ParamType::FixedBytes(size) => {
486 let bytes = data[offset..offset + size].to_vec();
487 Ok(ArgumentValue::FixedBytes(bytes))
488 }
489 ParamType::Bytes => {
490 let len = read_u256_as_usize(data, offset)?;
491 let start = offset + 32;
492 ensure_bytes(data, start, len)?;
493 Ok(ArgumentValue::Bytes(data[start..start + len].to_vec()))
494 }
495 ParamType::String => {
496 let len = read_u256_as_usize(data, offset)?;
497 let start = offset + 32;
498 ensure_bytes(data, start, len)?;
499 let s = std::str::from_utf8(&data[start..start + len])
500 .map_err(|e| DecodeError::InvalidEncoding(format!("invalid UTF-8: {e}")))?;
501 Ok(ArgumentValue::String(s.to_string()))
502 }
503 ParamType::Array(inner) => {
504 let len = read_u256_as_usize(data, offset)?;
505 let elements_start = offset + 32;
506 decode_array_elements(inner, data, elements_start, len, elements_start)
507 }
508 ParamType::FixedArray(inner, len) => {
509 decode_array_elements(inner, data, offset, *len, offset)
510 }
511 ParamType::Tuple(members) => {
512 let mut values = Vec::with_capacity(members.len());
513 let mut member_offset = offset;
514 for (name, member_type) in members {
516 let value = decode_value(member_type, data, member_offset, offset)?;
517 values.push((name.clone(), value));
518 member_offset += member_type.head_size();
519 }
520 Ok(ArgumentValue::Tuple(values))
521 }
522 }
523}
524
525fn decode_array_elements(
526 inner: &ParamType,
527 data: &[u8],
528 offset: usize,
529 len: usize,
530 base_offset: usize,
531) -> Result<ArgumentValue, DecodeError> {
532 let mut values = Vec::with_capacity(len);
533 let mut elem_offset = offset;
534 let step = inner.head_size();
535 for _ in 0..len {
536 let value = decode_value(inner, data, elem_offset, base_offset)?;
537 values.push(value);
538 elem_offset += step;
539 }
540 Ok(ArgumentValue::Array(values))
541}
542
543fn read_u256_as_usize(data: &[u8], offset: usize) -> Result<usize, DecodeError> {
544 ensure_bytes(data, offset, 32)?;
545 let word = &data[offset..offset + 32];
546 for &b in &word[..24] {
548 if b != 0 {
549 return Err(DecodeError::InvalidEncoding(
550 "offset too large for usize".to_string(),
551 ));
552 }
553 }
554 let mut bytes = [0u8; 8];
555 bytes.copy_from_slice(&word[24..32]);
556 Ok(u64::from_be_bytes(bytes) as usize)
557}
558
559fn ensure_bytes(data: &[u8], offset: usize, len: usize) -> Result<(), DecodeError> {
560 if offset + len > data.len() {
561 Err(DecodeError::CalldataTooShort {
562 expected: offset + len,
563 actual: data.len(),
564 })
565 } else {
566 Ok(())
567 }
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573
574 #[test]
575 fn test_parse_simple_signature() {
576 let sig = parse_signature("transfer(address,uint256)").unwrap();
577 assert_eq!(sig.name, "transfer");
578 assert_eq!(sig.params.len(), 2);
579 assert_eq!(sig.params[0], ParamType::Address);
580 assert_eq!(sig.params[1], ParamType::Uint(256));
581 assert_eq!(sig.canonical, "transfer(address,uint256)");
582 }
583
584 #[test]
585 fn test_parse_no_params() {
586 let sig = parse_signature("pause()").unwrap();
587 assert_eq!(sig.name, "pause");
588 assert!(sig.params.is_empty());
589 }
590
591 #[test]
592 fn test_parse_tuple_signature() {
593 let sig = parse_signature("foo((address,uint256),bool)").unwrap();
594 assert_eq!(sig.params.len(), 2);
595 assert_eq!(
596 sig.params[0],
597 ParamType::Tuple(vec![
598 (None, ParamType::Address),
599 (None, ParamType::Uint(256))
600 ])
601 );
602 assert_eq!(sig.params[1], ParamType::Bool);
603 }
604
605 #[test]
606 fn test_parse_named_tuple_members() {
607 let sig = parse_signature("foo((uint256 value, uint256 deadline) permit)").unwrap();
608 assert_eq!(sig.params.len(), 1);
609 assert_eq!(sig.param_names[0], Some("permit".to_string()));
610 if let ParamType::Tuple(members) = &sig.params[0] {
611 assert_eq!(members.len(), 2);
612 assert_eq!(members[0].0, Some("value".to_string()));
613 assert_eq!(members[0].1, ParamType::Uint(256));
614 assert_eq!(members[1].0, Some("deadline".to_string()));
615 assert_eq!(members[1].1, ParamType::Uint(256));
616 } else {
617 panic!("expected Tuple");
618 }
619 }
620
621 #[test]
622 fn test_canonical_strips_tuple_names() {
623 let named = parse_signature("foo((uint256 value, uint256 deadline) permit)").unwrap();
624 let unnamed = parse_signature("foo((uint256,uint256))").unwrap();
625 assert_eq!(named.selector, unnamed.selector);
626 assert_eq!(named.canonical, unnamed.canonical);
627 }
628
629 #[test]
630 fn test_parse_array_types() {
631 let sig = parse_signature("foo(uint256[],address[3])").unwrap();
632 assert_eq!(
633 sig.params[0],
634 ParamType::Array(Box::new(ParamType::Uint(256)))
635 );
636 assert_eq!(
637 sig.params[1],
638 ParamType::FixedArray(Box::new(ParamType::Address), 3)
639 );
640 }
641
642 #[test]
643 fn test_selector_computation() {
644 let sig = parse_signature("transfer(address,uint256)").unwrap();
646 assert_eq!(hex::encode(sig.selector), "a9059cbb");
647 }
648
649 #[test]
650 fn test_decode_transfer_calldata() {
651 let sig = parse_signature("transfer(address,uint256)").unwrap();
652
653 let mut calldata = Vec::new();
654 calldata.extend_from_slice(&sig.selector);
655 let mut addr_word = [0u8; 32];
657 addr_word[31] = 1;
658 calldata.extend_from_slice(&addr_word);
659 let mut amount_word = [0u8; 32];
661 amount_word[30] = 0x03;
662 amount_word[31] = 0xe8;
663 calldata.extend_from_slice(&amount_word);
664
665 let decoded = decode_calldata(&sig, &calldata).unwrap();
666 assert_eq!(decoded.function_name, "transfer");
667 assert_eq!(decoded.args.len(), 2);
668
669 if let ArgumentValue::Address(addr) = &decoded.args[0].value {
670 assert_eq!(addr[19], 1);
671 } else {
672 panic!("expected Address");
673 }
674
675 if let ArgumentValue::Uint(bytes) = &decoded.args[1].value {
676 assert_eq!(bytes[30], 0x03);
677 assert_eq!(bytes[31], 0xe8);
678 } else {
679 panic!("expected Uint");
680 }
681 }
682
683 #[test]
684 fn test_decode_bool() {
685 let sig = parse_signature("setApproval(bool)").unwrap();
686 let mut calldata = Vec::new();
687 calldata.extend_from_slice(&sig.selector);
688 let mut word = [0u8; 32];
689 word[31] = 1;
690 calldata.extend_from_slice(&word);
691
692 let decoded = decode_calldata(&sig, &calldata).unwrap();
693 if let ArgumentValue::Bool(b) = decoded.args[0].value {
694 assert!(b);
695 } else {
696 panic!("expected Bool");
697 }
698 }
699
700 #[test]
701 fn test_selector_mismatch() {
702 let sig = parse_signature("transfer(address,uint256)").unwrap();
703 let calldata = [0u8; 36]; let result = decode_calldata(&sig, &calldata);
705 assert!(result.is_err());
706 }
707
708 #[test]
709 fn test_parse_all_basic_types() {
710 let sig = parse_signature("f(address,uint256,int128,bool,bytes,bytes32,string)").unwrap();
711 assert_eq!(sig.params[0], ParamType::Address);
712 assert_eq!(sig.params[1], ParamType::Uint(256));
713 assert_eq!(sig.params[2], ParamType::Int(128));
714 assert_eq!(sig.params[3], ParamType::Bool);
715 assert_eq!(sig.params[4], ParamType::Bytes);
716 assert_eq!(sig.params[5], ParamType::FixedBytes(32));
717 assert_eq!(sig.params[6], ParamType::String);
718 }
719
720 #[test]
721 fn test_default_uint_int() {
722 let sig = parse_signature("f(uint,int)").unwrap();
723 assert_eq!(sig.params[0], ParamType::Uint(256));
724 assert_eq!(sig.params[1], ParamType::Int(256));
725 }
726
727 #[test]
728 fn test_parse_named_params() {
729 let sig = parse_signature(
730 "deposit(address asset,uint256 amount,address onBehalfOf,uint16 referralCode)",
731 )
732 .unwrap();
733 assert_eq!(sig.name, "deposit");
734 assert_eq!(sig.params.len(), 4);
735 assert_eq!(sig.params[0], ParamType::Address);
736 assert_eq!(sig.params[1], ParamType::Uint(256));
737 assert_eq!(sig.params[2], ParamType::Address);
738 assert_eq!(sig.params[3], ParamType::Uint(16));
739 assert_eq!(
740 sig.param_names,
741 vec![
742 Some("asset".to_string()),
743 Some("amount".to_string()),
744 Some("onBehalfOf".to_string()),
745 Some("referralCode".to_string()),
746 ]
747 );
748 assert_eq!(sig.canonical, "deposit(address,uint256,address,uint16)");
750 }
751
752 #[test]
753 fn test_parse_mixed_named_unnamed() {
754 let sig = parse_signature("f(address,uint256 amount)").unwrap();
755 assert_eq!(sig.param_names, vec![None, Some("amount".to_string())]);
756 }
757
758 #[test]
759 fn test_named_params_selector_unchanged() {
760 let named = parse_signature(
761 "deposit(address asset,uint256 amount,address onBehalfOf,uint16 referralCode)",
762 )
763 .unwrap();
764 let unnamed = parse_signature("deposit(address,uint256,address,uint16)").unwrap();
765 assert_eq!(named.selector, unnamed.selector);
766 assert_eq!(named.canonical, unnamed.canonical);
767 }
768}