1use alloy_dyn_abi::{DynSolType, DynSolValue, JsonAbiExt};
23use alloy_json_abi::Function;
24use alloy_primitives::{Address, Bytes, FixedBytes, I256, U256};
25use eyre::{eyre, Result};
26use std::collections::BTreeMap;
27
28pub fn encode_function_call(
58 functions: &BTreeMap<String, Vec<Function>>,
59 call_text: &str,
60) -> Result<Bytes> {
61 let (function_name, args_str) = parse_function_call_text(call_text)?;
62
63 let (function, args) = find_matching_function(functions, &function_name, &args_str)?;
65
66 encode_function_data(&function, args)
68}
69
70fn parse_function_call_text(call_text: &str) -> Result<(String, String)> {
74 let call_text = call_text.trim();
75
76 if let Some(open_paren) = call_text.find('(') {
77 if !call_text.ends_with(')') {
78 return Err(eyre!("Function call must end with ')': {}", call_text));
79 }
80
81 let function_name = call_text[..open_paren].trim().to_string();
82 let args_str = call_text[open_paren + 1..call_text.len() - 1].trim().to_string();
83
84 if function_name.is_empty() {
85 return Err(eyre!("Function name cannot be empty"));
86 }
87
88 Ok((function_name, args_str))
89 } else {
90 Err(eyre!("Invalid function call format. Expected format: functionName(arg1,arg2,...)"))
91 }
92}
93
94fn find_matching_function(
96 functions: &BTreeMap<String, Vec<Function>>,
97 function_name: &str,
98 args_str: &str,
99) -> Result<(Function, Vec<DynSolValue>)> {
100 let function_overloads = functions
101 .get(function_name)
102 .ok_or_else(|| eyre!("Function '{}' not found in ABI", function_name))?;
103
104 if function_overloads.is_empty() {
105 return Err(eyre!("No function definitions found for '{}'", function_name));
106 }
107
108 let mut parse_errors = Vec::new();
110
111 for function in function_overloads {
112 match parse_function_arguments(function, args_str) {
113 Ok(args) => {
114 return Ok((function.clone(), args));
116 }
117 Err(e) => {
118 parse_errors.push(format!(
120 "Function '{}({})': {}",
121 function.name,
122 function
123 .inputs
124 .iter()
125 .map(|param| param.ty.clone())
126 .collect::<Vec<_>>()
127 .join(","),
128 e
129 ));
130 }
131 }
132 }
133
134 let error_msg = if function_overloads.len() == 1 {
136 format!("Failed to parse arguments for function '{}': {}", function_name, parse_errors[0])
137 } else {
138 format!(
139 "Failed to match arguments with any overload of function '{}'. Tried:\n{}",
140 function_name,
141 parse_errors.join("\n")
142 )
143 };
144
145 Err(eyre!(error_msg))
146}
147
148fn parse_function_arguments(function: &Function, args_str: &str) -> Result<Vec<DynSolValue>> {
150 if args_str.trim().is_empty() {
151 if function.inputs.is_empty() {
152 return Ok(vec![]);
153 } else {
154 return Err(eyre!(
155 "Function '{}' expects {} arguments, but none provided",
156 function.name,
157 function.inputs.len()
158 ));
159 }
160 }
161
162 let arg_strings = split_arguments(args_str)?;
163
164 if arg_strings.len() != function.inputs.len() {
165 return Err(eyre!(
166 "Function '{}' expects {} arguments, but {} provided",
167 function.name,
168 function.inputs.len(),
169 arg_strings.len()
170 ));
171 }
172
173 let mut args = Vec::new();
174 for (i, (arg_str, param)) in arg_strings.iter().zip(&function.inputs).enumerate() {
175 let arg_value = parse_argument_value(arg_str.trim(), ¶m.ty)
176 .map_err(|e| eyre!("Failed to parse argument {}: {}", i + 1, e))?;
177 args.push(arg_value);
178 }
179
180 Ok(args)
181}
182
183fn split_arguments(args_str: &str) -> Result<Vec<String>> {
185 let mut args = Vec::new();
186 let mut current_arg = String::new();
187 let mut depth = 0; let mut in_string = false;
189 let mut escape_next = false;
190 let mut string_char = '\0';
191
192 for ch in args_str.chars() {
193 if escape_next {
194 current_arg.push(ch);
195 escape_next = false;
196 continue;
197 }
198
199 match ch {
200 '\\' if in_string => {
201 escape_next = true;
202 current_arg.push(ch);
203 }
204 '"' | '\'' => {
205 if !in_string {
206 in_string = true;
207 string_char = ch;
208 } else if ch == string_char {
209 in_string = false;
210 }
211 current_arg.push(ch);
212 }
213 '(' | '[' | '{' if !in_string => {
215 depth += 1;
216 current_arg.push(ch);
217 }
218 ')' | ']' | '}' if !in_string => {
220 depth -= 1;
221 current_arg.push(ch);
222 }
223 ',' if !in_string && depth == 0 => {
224 args.push(current_arg.trim().to_string());
225 current_arg.clear();
226 }
227 _ => {
228 current_arg.push(ch);
229 }
230 }
231 }
232
233 if !current_arg.trim().is_empty() {
234 args.push(current_arg.trim().to_string());
235 }
236
237 Ok(args)
238}
239
240fn parse_argument_value(arg_str: &str, param_type: &str) -> Result<DynSolValue> {
242 let arg_str = arg_str.trim();
243
244 let (cast_type, actual_value) = extract_type_cast(arg_str)?;
246
247 let value_to_parse = if let Some(cast_type) = cast_type {
249 validate_type_cast(&cast_type, param_type)?;
250 actual_value
251 } else {
252 arg_str
253 };
254
255 let sol_type = DynSolType::parse(param_type)
256 .map_err(|e| eyre!("Invalid parameter type '{}': {}", param_type, e))?;
257
258 match sol_type {
259 DynSolType::Address => {
260 let address = parse_address(value_to_parse)?;
261 Ok(DynSolValue::Address(address))
262 }
263 DynSolType::Uint(size) => {
264 let value = parse_uint(value_to_parse, size)?;
265 Ok(DynSolValue::Uint(value, size))
266 }
267 DynSolType::Int(size) => {
268 let value = parse_int(value_to_parse, size)?;
269 Ok(DynSolValue::Int(value, size))
270 }
271 DynSolType::Bool => {
272 let value = parse_bool(value_to_parse)?;
273 Ok(DynSolValue::Bool(value))
274 }
275 DynSolType::String => {
276 let value = parse_string(value_to_parse)?;
277 Ok(DynSolValue::String(value))
278 }
279 DynSolType::Bytes => {
280 let value = parse_bytes(value_to_parse)?;
281 Ok(DynSolValue::Bytes(value))
282 }
283 DynSolType::FixedBytes(size) => {
284 let value = parse_fixed_bytes(value_to_parse, size)?;
285 let mut word = [0u8; 32];
287 let copy_len = value.len().min(32);
288 word[..copy_len].copy_from_slice(&value[..copy_len]);
289 Ok(DynSolValue::FixedBytes(FixedBytes::from(word), size))
290 }
291 DynSolType::Array(ref inner) => parse_array(value_to_parse, inner),
292 DynSolType::FixedArray(ref inner, size) => parse_fixed_array(value_to_parse, inner, size),
293 DynSolType::Tuple(ref types) => parse_tuple(value_to_parse, types),
294 _ => Err(eyre!("Unsupported type: {}", param_type)),
295 }
296}
297
298fn extract_type_cast(s: &str) -> Result<(Option<String>, &str)> {
300 let s = s.trim();
301
302 let type_prefixes = [
305 "uint", "int", "address", "bool", "bytes", "string", "uint8", "uint16", "uint24", "uint32",
306 "uint40", "uint48", "uint56", "uint64", "uint72", "uint80", "uint88", "uint96", "uint104",
307 "uint112", "uint120", "uint128", "uint136", "uint144", "uint152", "uint160", "uint168",
308 "uint176", "uint184", "uint192", "uint200", "uint208", "uint216", "uint224", "uint232",
309 "uint240", "uint248", "uint256", "int8", "int16", "int24", "int32", "int40", "int48",
310 "int56", "int64", "int72", "int80", "int88", "int96", "int104", "int112", "int120",
311 "int128", "int136", "int144", "int152", "int160", "int168", "int176", "int184", "int192",
312 "int200", "int208", "int216", "int224", "int232", "int240", "int248", "int256", "bytes1",
313 "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10",
314 "bytes11", "bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18",
315 "bytes19", "bytes20", "bytes21", "bytes22", "bytes23", "bytes24", "bytes25", "bytes26",
316 "bytes27", "bytes28", "bytes29", "bytes30", "bytes31", "bytes32",
317 ];
318
319 for prefix in &type_prefixes {
320 if let Some(after_type) = s.strip_prefix(prefix) {
321 if after_type.starts_with('(') {
322 let mut depth = 0;
324 let mut end_idx = None;
325
326 for (i, ch) in after_type.chars().enumerate() {
327 match ch {
328 '(' => depth += 1,
329 ')' => {
330 depth -= 1;
331 if depth == 0 {
332 end_idx = Some(i);
333 break;
334 }
335 }
336 _ => {}
337 }
338 }
339
340 if let Some(end) = end_idx {
341 let value = &after_type[1..end];
342 return Ok((Some(prefix.to_string()), value));
343 }
344 }
345 }
346 }
347
348 Ok((None, s))
349}
350
351fn validate_type_cast(cast_type: &str, param_type: &str) -> Result<()> {
353 let normalize_type = |t: &str| -> String {
355 if t == "uint" {
356 "uint256".to_string()
357 } else if t == "int" {
358 "int256".to_string()
359 } else {
360 t.to_string()
361 }
362 };
363
364 let cast_normalized = normalize_type(cast_type);
365 let param_normalized = normalize_type(param_type);
366
367 if cast_normalized == param_normalized {
369 return Ok(());
370 }
371
372 if (cast_normalized.starts_with("uint") && param_normalized.starts_with("uint"))
374 || (cast_normalized.starts_with("int") && param_normalized.starts_with("int"))
375 {
376 return Ok(());
377 }
378
379 if cast_normalized.starts_with("bytes") && param_normalized.starts_with("bytes") {
381 return Ok(());
382 }
383
384 Err(eyre!(
385 "Type cast '{cast_type}' is not compatible with expected parameter type '{param_type}'"
386 ))
387}
388
389fn parse_address(s: &str) -> Result<Address> {
391 let s = s.trim();
392 if s.starts_with("0x") || s.starts_with("0X") {
393 s.parse().map_err(|e| eyre!("Invalid address '{s}': {e}"))
394 } else {
395 format!("0x{s}").parse().map_err(|e| eyre!("Invalid address '{s}': {e}"))
397 }
398}
399
400fn parse_uint(s: &str, _size: usize) -> Result<U256> {
402 let s = s.trim();
403 if s.starts_with("0x") || s.starts_with("0X") {
404 U256::from_str_radix(&s[2..], 16).map_err(|e| eyre!("Invalid hex uint '{}': {}", s, e))
405 } else {
406 U256::from_str_radix(s, 10).map_err(|e| eyre!("Invalid decimal uint '{}': {}", s, e))
407 }
408}
409
410fn parse_int(s: &str, _size: usize) -> Result<I256> {
412 let s = s.trim();
413 if s.starts_with("0x") || s.starts_with("0X") {
414 let uint_val = U256::from_str_radix(&s[2..], 16)
416 .map_err(|e| eyre!("Invalid hex int '{}': {}", s, e))?;
417 Ok(I256::from_raw(uint_val))
418 } else {
419 if let Some(positive_part) = s.strip_prefix('-') {
421 let uint_val = U256::from_str_radix(positive_part, 10)
422 .map_err(|e| eyre!("Invalid decimal int '{}': {}", s, e))?;
423 Ok(-I256::from_raw(uint_val))
424 } else {
425 let uint_val = U256::from_str_radix(s, 10)
426 .map_err(|e| eyre!("Invalid decimal int '{}': {}", s, e))?;
427 Ok(I256::from_raw(uint_val))
428 }
429 }
430}
431
432fn parse_bool(s: &str) -> Result<bool> {
434 match s.trim().to_lowercase().as_str() {
435 "true" | "1" => Ok(true),
436 "false" | "0" => Ok(false),
437 _ => Err(eyre!("Invalid boolean value '{}'. Expected 'true', 'false', '1', or '0'", s)),
438 }
439}
440
441fn parse_string(s: &str) -> Result<String> {
443 let s = s.trim();
444 if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
445 Ok(s[1..s.len() - 1].to_string())
446 } else {
447 Ok(s.to_string())
448 }
449}
450
451fn parse_bytes(s: &str) -> Result<Vec<u8>> {
453 let s = s.trim();
454 if s.starts_with("0x") || s.starts_with("0X") {
455 hex::decode(&s[2..]).map_err(|e| eyre!("Invalid hex bytes '{}': {}", s, e))
456 } else {
457 hex::decode(s).map_err(|e| eyre!("Invalid hex bytes '{}': {}", s, e))
458 }
459}
460
461fn parse_fixed_bytes(s: &str, size: usize) -> Result<Vec<u8>> {
463 let bytes = parse_bytes(s)?;
464 if bytes.len() != size {
465 return Err(eyre!(
466 "Fixed bytes size mismatch: expected {} bytes, got {}",
467 size,
468 bytes.len()
469 ));
470 }
471 Ok(bytes)
472}
473
474fn parse_array(s: &str, inner_type: &DynSolType) -> Result<DynSolValue> {
476 let s = s.trim();
477 if !s.starts_with('[') || !s.ends_with(']') {
478 return Err(eyre!("Array must be enclosed in square brackets: {}", s));
479 }
480
481 let inner_str = &s[1..s.len() - 1];
482 if inner_str.trim().is_empty() {
483 return Ok(DynSolValue::Array(vec![]));
484 }
485
486 let elements_str = split_arguments(inner_str)?;
487 let mut elements = Vec::new();
488
489 for element_str in elements_str {
490 let element = parse_argument_value(&element_str, &inner_type.to_string())?;
491 elements.push(element);
492 }
493
494 Ok(DynSolValue::Array(elements))
495}
496
497fn parse_fixed_array(s: &str, inner_type: &DynSolType, size: usize) -> Result<DynSolValue> {
499 if let DynSolValue::Array(elements) = parse_array(s, inner_type)? {
500 if elements.len() != size {
501 return Err(eyre!(
502 "Fixed array size mismatch: expected {} elements, got {}",
503 size,
504 elements.len()
505 ));
506 }
507 Ok(DynSolValue::FixedArray(elements))
508 } else {
509 unreachable!("parse_array should always return Array variant")
510 }
511}
512
513fn parse_tuple(s: &str, types: &[DynSolType]) -> Result<DynSolValue> {
515 let s = s.trim();
516
517 let (is_struct_syntax, inner_str) = if s.starts_with('{') && s.ends_with('}') {
519 (true, &s[1..s.len() - 1])
520 } else if s.starts_with('(') && s.ends_with(')') {
521 (false, &s[1..s.len() - 1])
522 } else {
523 return Err(eyre!("Tuple/Struct must be enclosed in parentheses () or braces {{}}: {}", s));
524 };
525
526 if inner_str.trim().is_empty() {
527 if types.is_empty() {
528 return Ok(DynSolValue::Tuple(vec![]));
529 } else {
530 return Err(eyre!("Empty tuple/struct provided but {} elements expected", types.len()));
531 }
532 }
533
534 if is_struct_syntax {
535 parse_struct_syntax(inner_str, types)
537 } else {
538 parse_positional_syntax(inner_str, types)
540 }
541}
542
543fn parse_struct_syntax(inner_str: &str, types: &[DynSolType]) -> Result<DynSolValue> {
545 let elements_str = split_arguments(inner_str)?;
549
550 if elements_str.len() != types.len() {
551 return Err(eyre!(
552 "Struct element count mismatch: expected {} elements, got {}",
553 types.len(),
554 elements_str.len()
555 ));
556 }
557
558 let mut elements = Vec::new();
559 for (element_str, element_type) in elements_str.iter().zip(types) {
560 let value_str = if element_str.contains(':') {
562 let parts: Vec<&str> = element_str.splitn(2, ':').collect();
564 if parts.len() == 2 {
565 parts[1].trim()
566 } else {
567 element_str.trim()
568 }
569 } else {
570 element_str.trim()
571 };
572
573 let element = parse_argument_value(value_str, &element_type.to_string())?;
574 elements.push(element);
575 }
576
577 Ok(DynSolValue::Tuple(elements))
578}
579
580fn parse_positional_syntax(inner_str: &str, types: &[DynSolType]) -> Result<DynSolValue> {
582 let elements_str = split_arguments(inner_str)?;
583 if elements_str.len() != types.len() {
584 return Err(eyre!(
585 "Tuple element count mismatch: expected {} elements, got {}",
586 types.len(),
587 elements_str.len()
588 ));
589 }
590
591 let mut elements = Vec::new();
592 for (element_str, element_type) in elements_str.iter().zip(types) {
593 let element = parse_argument_value(element_str, &element_type.to_string())?;
594 elements.push(element);
595 }
596
597 Ok(DynSolValue::Tuple(elements))
598}
599
600fn encode_function_data(function: &Function, args: Vec<DynSolValue>) -> Result<Bytes> {
602 let encoded_args = if args.is_empty() {
604 Vec::new()
605 } else {
606 function
607 .abi_encode_input(&args)
608 .map_err(|e| eyre!("Failed to encode function arguments: {}", e))?
609 };
610
611 let selector = function.selector();
613 let mut result = selector.to_vec();
614 result.extend_from_slice(&encoded_args);
615
616 Ok(result.into())
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622 use alloy_json_abi::{Param, StateMutability};
623
624 fn create_test_function(name: &str, inputs: Vec<(&str, &str)>) -> Function {
625 Function {
626 name: name.to_string(),
627 inputs: inputs
628 .into_iter()
629 .map(|(name, ty)| Param {
630 name: name.to_string(),
631 ty: ty.to_string(),
632 internal_type: None, components: vec![],
634 })
635 .collect(),
636 outputs: vec![],
637 state_mutability: StateMutability::NonPayable,
638 }
639 }
640
641 #[test]
642 fn test_parse_function_call_text() {
643 assert_eq!(
644 parse_function_call_text("balanceOf(0x123)").unwrap(),
645 ("balanceOf".to_string(), "0x123".to_string())
646 );
647
648 assert_eq!(
649 parse_function_call_text("transfer(0x123, 100)").unwrap(),
650 ("transfer".to_string(), "0x123, 100".to_string())
651 );
652
653 assert_eq!(
654 parse_function_call_text("noArgs()").unwrap(),
655 ("noArgs".to_string(), String::new())
656 );
657 }
658
659 #[test]
660 fn test_split_arguments() {
661 assert_eq!(split_arguments("0x123, 100").unwrap(), vec!["0x123", "100"]);
662
663 assert_eq!(
664 split_arguments("0x123, [1,2,3], \"hello, world\"").unwrap(),
665 vec!["0x123", "[1,2,3]", "\"hello, world\""]
666 );
667
668 assert_eq!(
670 split_arguments("{field1: 123, field2: \"test\"}, 456").unwrap(),
671 vec!["{field1: 123, field2: \"test\"}", "456"]
672 );
673
674 assert_eq!(
676 split_arguments("0x123, {inner: [1,2,3], value: 100}").unwrap(),
677 vec!["0x123", "{inner: [1,2,3], value: 100}"]
678 );
679 }
680
681 #[test]
682 fn test_parse_address() {
683 let addr = parse_address("0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1").unwrap();
684 assert_eq!(addr.to_string().to_lowercase(), "0x742d35cc6634c0532925a3b8d6ac6e89e86c6ad1");
686 }
687
688 #[test]
689 fn test_parse_uint() {
690 assert_eq!(parse_uint("123", 256).unwrap(), U256::from(123u64));
691 assert_eq!(parse_uint("0xff", 256).unwrap(), U256::from(255u64));
692 }
693
694 #[test]
695 fn test_encode_simple_function_call() {
696 let mut functions = BTreeMap::new();
697 let balance_of = create_test_function("balanceOf", vec![("account", "address")]);
698 functions.insert("balanceOf".to_string(), vec![balance_of]);
699
700 let encoded = encode_function_call(
701 &functions,
702 "balanceOf(0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1)",
703 )
704 .unwrap();
705
706 assert_eq!(&encoded[0..4], &[0x70, 0xa0, 0x82, 0x31]);
708 }
709
710 #[test]
711 fn test_function_overload_resolution() {
712 let mut functions = BTreeMap::new();
713
714 let transfer_address_uint =
716 create_test_function("transfer", vec![("to", "address"), ("amount", "uint256")]);
717 let transfer_uint_address =
718 create_test_function("transfer", vec![("tokenId", "uint256"), ("to", "address")]);
719
720 functions
721 .insert("transfer".to_string(), vec![transfer_address_uint, transfer_uint_address]);
722
723 let encoded1 = encode_function_call(
725 &functions,
726 "transfer(0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1, 100)",
727 )
728 .unwrap();
729
730 let encoded2 = encode_function_call(
732 &functions,
733 "transfer(123, 0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1)",
734 )
735 .unwrap();
736
737 assert_ne!(&encoded1[0..4], &encoded2[0..4]);
739 }
740
741 #[test]
742 fn test_overload_resolution_failure() {
743 let mut functions = BTreeMap::new();
744 let balance_of = create_test_function("balanceOf", vec![("account", "address")]);
745 functions.insert("balanceOf".to_string(), vec![balance_of]);
746
747 let result = encode_function_call(
749 &functions,
750 "balanceOf(123)", );
752
753 assert!(result.is_err());
754 let error_msg = result.unwrap_err().to_string();
755 assert!(error_msg.contains("Failed to parse arguments"));
756 }
757
758 #[test]
759 fn test_struct_syntax_parsing() {
760 let mut functions = BTreeMap::new();
761 let submit_data = create_test_function("submitData", vec![("data", "(address,uint256)")]);
763 functions.insert("submitData".to_string(), vec![submit_data]);
764
765 let encoded1 = encode_function_call(
767 &functions,
768 "submitData({user: 0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1, amount: 100})",
769 );
770 assert!(encoded1.is_ok(), "Struct syntax should work: {:?}", encoded1.err());
771
772 let encoded2 = encode_function_call(
774 &functions,
775 "submitData((0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1, 100))",
776 );
777 assert!(encoded2.is_ok(), "Tuple syntax should work: {:?}", encoded2.err());
778
779 assert_eq!(encoded1.unwrap(), encoded2.unwrap());
781 }
782
783 #[test]
784 fn test_nested_struct_parsing() {
785 let result = split_arguments("0x123, {field1: {nested: 456}, field2: [1,2,3]}");
787 assert!(result.is_ok());
788 let args = result.unwrap();
789 assert_eq!(args.len(), 2);
790 assert_eq!(args[0], "0x123");
791 assert_eq!(args[1], "{field1: {nested: 456}, field2: [1,2,3]}");
792 }
793
794 #[test]
795 fn test_type_cast_extraction() {
796 assert_eq!(
798 extract_type_cast("uint256(123)").unwrap(),
799 (Some("uint256".to_string()), "123")
800 );
801
802 assert_eq!(
804 extract_type_cast("address(0x123)").unwrap(),
805 (Some("address".to_string()), "0x123")
806 );
807
808 assert_eq!(extract_type_cast("uint(42)").unwrap(), (Some("uint".to_string()), "42"));
810
811 assert_eq!(
813 extract_type_cast("bytes32(0xabc)").unwrap(),
814 (Some("bytes32".to_string()), "0xabc")
815 );
816
817 assert_eq!(
819 extract_type_cast("uint256((1 + 2))").unwrap(),
820 (Some("uint256".to_string()), "(1 + 2)")
821 );
822
823 assert_eq!(extract_type_cast("123").unwrap(), (None, "123"));
825
826 assert_eq!(extract_type_cast("(123)").unwrap(), (None, "(123)"));
828 }
829
830 #[test]
831 fn test_type_cast_with_function_call() {
832 let mut functions = BTreeMap::new();
833 let transfer =
834 create_test_function("transfer", vec![("to", "address"), ("amount", "uint256")]);
835 functions.insert("transfer".to_string(), vec![transfer]);
836
837 let result = encode_function_call(
839 &functions,
840 "transfer(address(0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1), uint256(100))",
841 );
842 assert!(result.is_ok(), "Type casting should work: {:?}", result.err());
843
844 let result = encode_function_call(
846 &functions,
847 "transfer(address(0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1), uint(100))",
848 );
849 assert!(result.is_ok(), "Shorthand uint cast should work: {:?}", result.err());
850 }
851
852 #[test]
853 fn test_complex_nested_calls() {
854 let mut functions = BTreeMap::new();
855 let complex =
857 create_test_function("complexCall", vec![("data", "((address,uint256),bytes32[])")]);
858 functions.insert("complexCall".to_string(), vec![complex]);
859
860 let result = encode_function_call(
862 &functions,
863 "complexCall(((\
864 0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1, \
865 123\
866 ), [\
867 0x0000000000000000000000000000000000000000000000000000000000000001,\
868 0x0000000000000000000000000000000000000000000000000000000000000002\
869 ]))",
870 );
871 assert!(result.is_ok(), "Complex nested call should work: {:?}", result.err());
872 }
873
874 #[test]
875 fn test_array_parsing() {
876 let mut functions = BTreeMap::new();
877 let batch = create_test_function(
878 "batchTransfer",
879 vec![("recipients", "address[]"), ("amounts", "uint256[]")],
880 );
881 functions.insert("batchTransfer".to_string(), vec![batch]);
882
883 let result = encode_function_call(
885 &functions,
886 "batchTransfer(\
887 [0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1, 0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad2],\
888 [100, 200]\
889 )"
890 );
891 assert!(result.is_ok(), "Array parsing should work: {:?}", result.err());
892
893 let result = encode_function_call(
895 &functions,
896 "batchTransfer(\
897 [address(0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1), address(0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad2)],\
898 [uint256(100), uint256(200)]\
899 )"
900 );
901 assert!(result.is_ok(), "Arrays with type casts should work: {:?}", result.err());
902 }
903
904 #[test]
905 fn test_mixed_argument_types() {
906 let mut functions = BTreeMap::new();
907 let mixed = create_test_function(
908 "mixedTypes",
909 vec![("flag", "bool"), ("data", "bytes"), ("text", "string"), ("number", "int256")],
910 );
911 functions.insert("mixedTypes".to_string(), vec![mixed]);
912
913 let result =
915 encode_function_call(&functions, r#"mixedTypes(true, 0xabcdef, "hello world", -123)"#);
916 assert!(result.is_ok(), "Mixed types should work: {:?}", result.err());
917
918 let result = encode_function_call(
920 &functions,
921 r#"mixedTypes(bool(true), bytes(0xabcdef), string("hello world"), int256(-123))"#,
922 );
923 assert!(result.is_ok(), "Mixed types with casts should work: {:?}", result.err());
924 }
925
926 #[test]
927 fn test_edge_cases() {
928 let mut functions = BTreeMap::new();
929 let simple = create_test_function("test", vec![("value", "uint256")]);
930 functions.insert("test".to_string(), vec![simple]);
931
932 let result = encode_function_call(&functions, "test( 123 )");
934 assert!(result.is_ok());
935
936 let result = encode_function_call(&functions, "test(\t123\n)");
938 assert!(result.is_ok());
939
940 let result = encode_function_call(&functions, "test(0xff)");
942 assert!(result.is_ok());
943
944 let result = encode_function_call(&functions, "test(1e18)");
946 assert!(result.is_err());
947
948 let result = encode_function_call(&functions, "test(1_000_000)");
950 let _ = result;
954 }
955
956 #[test]
957 fn test_empty_and_single_arguments() {
958 let mut functions = BTreeMap::new();
959
960 let no_args = create_test_function("noArgs", vec![]);
962 functions.insert("noArgs".to_string(), vec![no_args]);
963
964 let single_arg = create_test_function("singleArg", vec![("value", "uint256")]);
966 functions.insert("singleArg".to_string(), vec![single_arg]);
967
968 assert!(encode_function_call(&functions, "noArgs()").is_ok());
970 assert!(encode_function_call(&functions, "noArgs( )").is_ok());
971 assert!(encode_function_call(&functions, "noArgs( )").is_ok());
972
973 assert!(encode_function_call(&functions, "singleArg(42)").is_ok());
975 assert!(encode_function_call(&functions, "singleArg( 42 )").is_ok());
976 assert!(encode_function_call(&functions, "singleArg(uint256(42))").is_ok());
977 }
978
979 #[test]
980 fn test_fixed_arrays() {
981 let mut functions = BTreeMap::new();
982 let fixed = create_test_function("fixedArray", vec![("values", "uint256[3]")]);
983 functions.insert("fixedArray".to_string(), vec![fixed]);
984
985 let result = encode_function_call(&functions, "fixedArray([1, 2, 3])");
987 assert!(result.is_ok());
988
989 let result = encode_function_call(&functions, "fixedArray([1, 2])");
991 assert!(result.is_err());
992 assert!(result.unwrap_err().to_string().contains("Fixed array size mismatch"));
993
994 let result = encode_function_call(&functions, "fixedArray([1, 2, 3, 4])");
996 assert!(result.is_err());
997 }
998
999 #[test]
1000 fn test_string_escaping() {
1001 let mut functions = BTreeMap::new();
1002 let string_fn = create_test_function("setString", vec![("text", "string")]);
1003 functions.insert("setString".to_string(), vec![string_fn]);
1004
1005 let result = encode_function_call(&functions, r#"setString("hello \"world\"")"#);
1007 assert!(result.is_ok());
1008
1009 let result = encode_function_call(&functions, r#"setString('hello world')"#);
1011 assert!(result.is_ok());
1012
1013 let result = encode_function_call(&functions, r#"setString("it's working")"#);
1015 assert!(result.is_ok());
1016
1017 let result = encode_function_call(&functions, r#"setString("hello, world")"#);
1019 assert!(result.is_ok());
1020
1021 let result = encode_function_call(&functions, r#"setString("test(123)")"#);
1023 assert!(result.is_ok());
1024
1025 let result = encode_function_call(&functions, r#"setString("{key: value}")"#);
1027 assert!(result.is_ok());
1028 }
1029
1030 #[test]
1031 fn test_invalid_type_casts() {
1032 let mut functions = BTreeMap::new();
1033 let transfer =
1034 create_test_function("transfer", vec![("to", "address"), ("amount", "uint256")]);
1035 functions.insert("transfer".to_string(), vec![transfer]);
1036
1037 let result =
1039 encode_function_call(&functions, r#"transfer(string("not an address"), uint256(100))"#);
1040 assert!(result.is_err());
1041 assert!(result.unwrap_err().to_string().contains("not compatible"));
1042
1043 let result = encode_function_call(
1045 &functions,
1046 "transfer(0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1, bool(true))",
1047 );
1048 assert!(result.is_err());
1049 }
1050
1051 #[test]
1052 fn test_deeply_nested_structures() {
1053 let mut functions = BTreeMap::new();
1054 let deep = create_test_function(
1056 "deeplyNested",
1057 vec![("data", "(uint256,(address,(bytes32,bool)[]))")],
1058 );
1059 functions.insert("deeplyNested".to_string(), vec![deep]);
1060
1061 let result = encode_function_call(
1062 &functions,
1063 "deeplyNested((123, (\
1064 0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1, \
1065 [(\
1066 0x0000000000000000000000000000000000000000000000000000000000000001, \
1067 true\
1068 ), (\
1069 0x0000000000000000000000000000000000000000000000000000000000000002, \
1070 false\
1071 )]\
1072 )))",
1073 );
1074 assert!(result.is_ok(), "Deeply nested structure should work: {:?}", result.err());
1075 }
1076
1077 #[test]
1078 fn test_all_solidity_types() {
1079 assert!(parse_uint("123", 256).is_ok());
1081 assert!(parse_uint("0xff", 256).is_ok());
1082 assert!(parse_uint("0", 256).is_ok());
1083
1084 assert!(parse_int("123", 256).is_ok());
1085 assert!(parse_int("-123", 256).is_ok());
1086 assert!(parse_int("0", 256).is_ok());
1087
1088 assert!(parse_bool("true").is_ok());
1089 assert!(parse_bool("false").is_ok());
1090 assert!(parse_bool("1").is_ok());
1091 assert!(parse_bool("0").is_ok());
1092
1093 assert!(parse_address("0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1").is_ok());
1094 assert!(parse_address("742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1").is_ok());
1095
1096 assert!(parse_bytes("0xabcdef").is_ok());
1097 assert!(parse_bytes("abcdef").is_ok());
1098 assert!(parse_bytes("0x").is_ok());
1099
1100 assert!(parse_string("hello world").is_ok());
1101 assert!(parse_string("\"quoted string\"").is_ok());
1102 assert!(parse_string("'single quoted'").is_ok());
1103 }
1104
1105 #[test]
1106 fn test_overload_with_structs() {
1107 let mut functions = BTreeMap::new();
1108
1109 let process1 = create_test_function("process", vec![("data", "(address,uint256)")]);
1111 let process2 = create_test_function("process", vec![("data", "(uint256,address)")]);
1112
1113 functions.insert("process".to_string(), vec![process1, process2]);
1114
1115 let result1 = encode_function_call(
1117 &functions,
1118 "process((0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1, 100))",
1119 );
1120 assert!(result1.is_ok());
1121
1122 let result2 = encode_function_call(
1124 &functions,
1125 "process((100, 0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1))",
1126 );
1127 assert!(result2.is_ok());
1128
1129 assert_ne!(result1.unwrap()[0..4], result2.unwrap()[0..4]);
1131 }
1132
1133 #[test]
1134 fn test_payable_and_nonpayable_functions() {
1135 let mut functions = BTreeMap::new();
1137 let send_eth = create_test_function("sendEth", vec![("to", "address")]);
1138 functions.insert("sendEth".to_string(), vec![send_eth]);
1139
1140 let result =
1141 encode_function_call(&functions, "sendEth(0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1)");
1142 assert!(result.is_ok());
1143 }
1144
1145 #[test]
1146 fn test_max_values() {
1147 let mut functions = BTreeMap::new();
1148 let max_test =
1149 create_test_function("maxTest", vec![("maxUint", "uint256"), ("maxInt", "int256")]);
1150 functions.insert("maxTest".to_string(), vec![max_test]);
1151
1152 let result = encode_function_call(
1154 &functions,
1155 "maxTest(\
1156 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, \
1157 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\
1158 )",
1159 );
1160 assert!(result.is_ok(), "Max values should work: {:?}", result.err());
1161
1162 let result = encode_function_call(
1164 &functions,
1165 "maxTest(\
1166 uint256(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff), \
1167 int256(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)\
1168 )",
1169 );
1170 assert!(result.is_ok());
1171 }
1172
1173 #[test]
1174 fn test_bytes_variations() {
1175 let mut functions = BTreeMap::new();
1176 let bytes_test = create_test_function(
1177 "bytesTest",
1178 vec![
1179 ("b1", "bytes1"),
1180 ("b2", "bytes2"),
1181 ("b4", "bytes4"),
1182 ("b8", "bytes8"),
1183 ("b16", "bytes16"),
1184 ("b32", "bytes32"),
1185 ],
1186 );
1187 functions.insert("bytesTest".to_string(), vec![bytes_test]);
1188
1189 let result = encode_function_call(
1190 &functions,
1191 "bytesTest(\
1192 0x01, \
1193 0x0102, \
1194 0x01020304, \
1195 0x0102030405060708, \
1196 0x01020304050607080910111213141516, \
1197 0x0102030405060708091011121314151617181920212223242526272829303132\
1198 )",
1199 );
1200 assert!(result.is_ok(), "Different bytes sizes should work: {:?}", result.err());
1201 }
1202
1203 #[test]
1204 fn test_function_with_multiple_arrays() {
1205 let mut functions = BTreeMap::new();
1206 let multi_array = create_test_function(
1207 "multiArray",
1208 vec![("arr1", "uint256[]"), ("arr2", "address[]"), ("arr3", "bool[]")],
1209 );
1210 functions.insert("multiArray".to_string(), vec![multi_array]);
1211
1212 let result = encode_function_call(
1213 &functions,
1214 "multiArray(\
1215 [1, 2, 3], \
1216 [0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1], \
1217 [true, false, true]\
1218 )",
1219 );
1220 assert!(result.is_ok(), "Multiple arrays should work: {:?}", result.err());
1221 }
1222
1223 #[test]
1224 fn test_empty_arrays_and_strings() {
1225 let mut functions = BTreeMap::new();
1226 let empty_test = create_test_function(
1227 "emptyTest",
1228 vec![("emptyArr", "uint256[]"), ("emptyStr", "string"), ("emptyBytes", "bytes")],
1229 );
1230 functions.insert("emptyTest".to_string(), vec![empty_test]);
1231
1232 let result = encode_function_call(&functions, r#"emptyTest([], "", 0x)"#);
1234 assert!(result.is_ok(), "Empty values should work: {:?}", result.err());
1235
1236 let result = encode_function_call(&functions, r#"emptyTest([], string(""), bytes(0x))"#);
1238 assert!(result.is_ok());
1239 }
1240
1241 #[test]
1242 fn test_special_characters_in_strings() {
1243 let mut functions = BTreeMap::new();
1244 let special = create_test_function("specialChars", vec![("text", "string")]);
1245 functions.insert("specialChars".to_string(), vec![special]);
1246
1247 let result =
1249 encode_function_call(&functions, r#"specialChars("line1\nline2\ttab\r\nwindows")"#);
1250 assert!(result.is_ok());
1251
1252 let result = encode_function_call(&functions, r#"specialChars("Hello δΈη π")"#);
1254 assert!(result.is_ok());
1255
1256 let result = encode_function_call(&functions, r#"specialChars("path\\to\\file")"#);
1258 assert!(result.is_ok());
1259 }
1260
1261 #[test]
1262 fn test_complex_overload_resolution() {
1263 let mut functions = BTreeMap::new();
1264
1265 let func1 = create_test_function("complex", vec![("a", "uint256")]);
1267 let func2 = create_test_function("complex", vec![("a", "uint256"), ("b", "uint256")]);
1268 let func3 = create_test_function("complex", vec![("a", "uint256"), ("b", "address")]);
1269 let func4 = create_test_function("complex", vec![("data", "(uint256,address)")]);
1270
1271 functions.insert("complex".to_string(), vec![func1, func2, func3, func4]);
1272
1273 assert!(encode_function_call(&functions, "complex(123)").is_ok());
1275
1276 assert!(encode_function_call(&functions, "complex(123, 456)").is_ok());
1278
1279 assert!(encode_function_call(
1281 &functions,
1282 "complex(123, 0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1)"
1283 )
1284 .is_ok());
1285
1286 assert!(encode_function_call(
1288 &functions,
1289 "complex((123, 0x742d35Cc6634C0532925a3b8D6Ac6E89e86C6Ad1))"
1290 )
1291 .is_ok());
1292 }
1293
1294 #[test]
1295 fn test_error_messages() {
1296 let mut functions = BTreeMap::new();
1297 let test_fn = create_test_function("test", vec![("value", "uint256")]);
1298 functions.insert("test".to_string(), vec![test_fn]);
1299
1300 let result = encode_function_call(&functions, "nonexistent(123)");
1302 assert!(result.is_err());
1303 assert!(result.unwrap_err().to_string().contains("not found"));
1304
1305 let result = encode_function_call(&functions, "test(0xnotahexvalue)");
1306 assert!(result.is_err());
1307
1308 let result = encode_function_call(&functions, "test(true)"); assert!(result.is_err());
1310 assert!(result.unwrap_err().to_string().contains("Failed to parse"));
1311
1312 let result = encode_function_call(&functions, "test"); assert!(result.is_err());
1314 assert!(result.unwrap_err().to_string().contains("Invalid function call format"));
1315
1316 let result = encode_function_call(&functions, "test("); assert!(result.is_err());
1318 }
1319
1320 #[test]
1321 fn test_zero_and_negative_values() {
1322 let mut functions = BTreeMap::new();
1323 let zero_test = create_test_function(
1324 "zeroTest",
1325 vec![("uintZero", "uint256"), ("intNeg", "int256"), ("intZero", "int256")],
1326 );
1327 functions.insert("zeroTest".to_string(), vec![zero_test]);
1328
1329 let result = encode_function_call(&functions, "zeroTest(0, -1, 0)");
1331 assert!(result.is_ok());
1332
1333 let result = encode_function_call(&functions, "zeroTest(0x0, -0x1, 0x00)");
1335 assert!(result.is_ok());
1336
1337 let result = encode_function_call(
1339 &functions,
1340 "zeroTest(0, -57896044618658097711785492504343953926634992332820282019728792003956564819968, 0)"
1341 );
1342 assert!(result.is_ok());
1343 }
1344}