Skip to main content

soldb_ethdebug/
abi.rs

1//! ABI encoding and function-signature parsing.
2//!
3//! This module turns a human-written signature such as `submitPerson((string,uint256))`
4//! plus its arguments into calldata, and derives the four-byte selector for it. Tuples,
5//! fixed and dynamic arrays, and the elementary value types are supported.
6//!
7//! It also carries the keccak-256 implementation used for selectors and event topics, so
8//! the workspace does not take a hashing dependency for this one purpose.
9
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use soldb_core::{SoldbError, SoldbResult};
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct FunctionSignature {
16    pub name: String,
17    pub arg_types: Vec<String>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct AbiInput {
22    #[serde(rename = "type")]
23    pub ty: String,
24    #[serde(default)]
25    pub components: Vec<AbiInput>,
26}
27
28pub fn match_abi_types(parsed_types: &[String], abi_types: &[String]) -> bool {
29    parsed_types.len() == abi_types.len()
30        && parsed_types
31            .iter()
32            .zip(abi_types)
33            .all(|(parsed_type, abi_type)| match_single_type(parsed_type, abi_type))
34}
35
36pub fn match_single_type(parsed_type: &str, abi_type: &str) -> bool {
37    if parsed_type == abi_type {
38        return true;
39    }
40
41    if parsed_type.starts_with('(') && parsed_type.ends_with(')') && abi_type == "tuple" {
42        return true;
43    }
44
45    if let (Some(parsed_base), Some(abi_base)) =
46        (parsed_type.strip_suffix("[]"), abi_type.strip_suffix("[]"))
47    {
48        return match_single_type(parsed_base, abi_base);
49    }
50
51    parsed_type.contains('(') && abi_type == "tuple"
52}
53
54pub fn parse_signature(signature: &str) -> Option<FunctionSignature> {
55    let open = signature.find('(')?;
56    if !signature.ends_with(')') {
57        return None;
58    }
59
60    let name = &signature[..open];
61    if name.is_empty() || !name.chars().all(|c| c == '_' || c.is_ascii_alphanumeric()) {
62        return None;
63    }
64
65    let args = &signature[open + 1..signature.len() - 1];
66    Some(FunctionSignature {
67        name: name.to_owned(),
68        arg_types: split_args(args)?,
69    })
70}
71
72pub fn parse_tuple_arg(value: &Value, abi_input: &AbiInput) -> SoldbResult<Value> {
73    let values = value
74        .as_array()
75        .ok_or_else(|| SoldbError::Message("Tuple argument must be an array".to_owned()))?;
76
77    if values.len() < abi_input.components.len() {
78        return Err(SoldbError::Message(format!(
79            "Tuple argument has {} values but ABI expects {}",
80            values.len(),
81            abi_input.components.len()
82        )));
83    }
84
85    let mut parsed = Vec::with_capacity(abi_input.components.len());
86    for (index, component) in abi_input.components.iter().enumerate() {
87        let component_value = &values[index];
88        if component.ty == "tuple" {
89            parsed.push(parse_tuple_arg(component_value, component)?);
90        } else if component.ty.starts_with("tuple") && component.ty.ends_with("[]") {
91            let tuple_array = component_value.as_array().ok_or_else(|| {
92                SoldbError::Message("Tuple array argument must be an array".to_owned())
93            })?;
94
95            let parsed_items = tuple_array
96                .iter()
97                .map(|item| parse_tuple_arg(item, component))
98                .collect::<SoldbResult<Vec<_>>>()?;
99            parsed.push(Value::Array(parsed_items));
100        } else {
101            parsed.push(component_value.clone());
102        }
103    }
104
105    Ok(Value::Array(parsed))
106}
107
108pub fn canonical_abi_input_type(input: &AbiInput) -> String {
109    if let Some(suffix) = input.ty.strip_prefix("tuple") {
110        let components = input
111            .components
112            .iter()
113            .map(canonical_abi_input_type)
114            .collect::<Vec<_>>()
115            .join(",");
116        return format!("({components}){suffix}");
117    }
118    input.ty.clone()
119}
120
121pub fn encode_abi_arguments(arg_types: &[String], args: &[String]) -> SoldbResult<String> {
122    if arg_types.len() != args.len() {
123        return Err(SoldbError::Message(format!(
124            "Wrong argument count: expected {}, got {}",
125            arg_types.len(),
126            args.len()
127        )));
128    }
129
130    let encoded_args = arg_types
131        .iter()
132        .zip(args)
133        .map(|(arg_type, arg_value)| encode_arg(arg_type, arg_value))
134        .collect::<SoldbResult<Vec<_>>>()?;
135    Ok(encode_tuple_payload(encoded_args))
136}
137
138pub fn encode_function_call(signature: &str, args: &[String]) -> SoldbResult<String> {
139    let parsed = parse_signature(signature)
140        .ok_or_else(|| SoldbError::Message(format!("Invalid function signature: {signature}")))?;
141
142    let selector = function_selector(signature)?;
143    let payload = encode_abi_arguments(&parsed.arg_types, args).map_err(|error| {
144        SoldbError::Message(format!(
145            "Failed to encode arguments for {signature}: {error}"
146        ))
147    })?;
148
149    let mut encoded = String::with_capacity(2 + 8 + payload.len());
150    encoded.push_str("0x");
151    encoded.push_str(&bytes_to_hex(&selector));
152    encoded.push_str(&payload);
153    Ok(encoded)
154}
155
156pub fn function_selector(signature: &str) -> SoldbResult<[u8; 4]> {
157    let parsed = parse_signature(signature)
158        .ok_or_else(|| SoldbError::Message(format!("Invalid function signature: {signature}")))?;
159    let canonical = format!("{}({})", parsed.name, parsed.arg_types.join(","));
160    let hash = keccak256(canonical.as_bytes());
161    Ok([hash[0], hash[1], hash[2], hash[3]])
162}
163
164pub fn keccak256(input: &[u8]) -> [u8; 32] {
165    const RATE: usize = 136;
166    let mut state = [0_u64; 25];
167    let (blocks, remainder) = input.as_chunks::<RATE>();
168
169    for block in blocks {
170        absorb_block(&mut state, block);
171        keccak_f1600(&mut state);
172    }
173
174    let mut final_block = [0_u8; RATE];
175    final_block[..remainder.len()].copy_from_slice(remainder);
176    final_block[remainder.len()] ^= 0x01;
177    final_block[RATE - 1] ^= 0x80;
178    absorb_block(&mut state, &final_block);
179    keccak_f1600(&mut state);
180
181    let mut output = [0_u8; 32];
182    for (index, chunk) in output.chunks_mut(8).enumerate() {
183        chunk.copy_from_slice(&state[index].to_le_bytes());
184    }
185    output
186}
187
188fn split_args(input: &str) -> Option<Vec<String>> {
189    if input.trim().is_empty() {
190        return Some(Vec::new());
191    }
192
193    let mut args = Vec::new();
194    let mut depth = 0_i64;
195    let mut current = String::new();
196
197    for ch in input.chars() {
198        match ch {
199            ',' if depth == 0 => {
200                let arg = current.trim();
201                if !arg.is_empty() {
202                    args.push(arg.to_owned());
203                }
204                current.clear();
205            }
206            '(' => {
207                depth += 1;
208                current.push(ch);
209            }
210            ')' => {
211                depth -= 1;
212                if depth < 0 {
213                    return None;
214                }
215                current.push(ch);
216            }
217            _ => current.push(ch),
218        }
219    }
220
221    if depth != 0 {
222        return None;
223    }
224
225    let arg = current.trim();
226    if !arg.is_empty() {
227        args.push(arg.to_owned());
228    }
229
230    Some(args)
231}
232
233enum EncodedArg {
234    Static(String),
235    Dynamic(String),
236}
237
238impl EncodedArg {
239    fn head_size_bytes(&self) -> usize {
240        match self {
241            Self::Static(words) => words.len() / 2,
242            Self::Dynamic(_) => 32,
243        }
244    }
245
246    fn is_dynamic(&self) -> bool {
247        matches!(self, Self::Dynamic(_))
248    }
249}
250
251fn encode_arg(arg_type: &str, value: &str) -> SoldbResult<EncodedArg> {
252    let arg_type = arg_type.trim();
253    if let Some((base_type, fixed_len)) = parse_array_type(arg_type)? {
254        return encode_array_arg(base_type, fixed_len, value);
255    }
256    if let Some(component_types) = parse_tuple_type(arg_type) {
257        return encode_tuple_arg(&component_types, value);
258    }
259    if arg_type == "string" {
260        return Ok(EncodedArg::Dynamic(encode_dynamic_bytes(value.as_bytes())));
261    }
262    if arg_type == "bytes" {
263        return Ok(EncodedArg::Dynamic(encode_dynamic_bytes(&parse_hex_bytes(
264            value,
265        )?)));
266    }
267
268    encode_static_arg(arg_type, value).map(EncodedArg::Static)
269}
270
271fn encode_tuple_payload(encoded_args: Vec<EncodedArg>) -> String {
272    let head_size_bytes = encoded_args
273        .iter()
274        .map(EncodedArg::head_size_bytes)
275        .sum::<usize>();
276    let tail_size = encoded_args
277        .iter()
278        .filter_map(|arg| match arg {
279            EncodedArg::Static(_) => None,
280            EncodedArg::Dynamic(tail) => Some(tail.len()),
281        })
282        .sum::<usize>();
283
284    let mut encoded = String::with_capacity(encoded_args.len() * 64 + tail_size);
285    let mut tails = String::with_capacity(tail_size);
286    let mut dynamic_offset = head_size_bytes;
287    for arg in encoded_args {
288        match arg {
289            EncodedArg::Static(word) => encoded.push_str(&word),
290            EncodedArg::Dynamic(tail) => {
291                encoded.push_str(&format!("{dynamic_offset:064x}"));
292                dynamic_offset += tail.len() / 2;
293                tails.push_str(&tail);
294            }
295        }
296    }
297    encoded.push_str(&tails);
298    encoded
299}
300
301fn parse_array_type(arg_type: &str) -> SoldbResult<Option<(&str, Option<usize>)>> {
302    if !arg_type.ends_with(']') {
303        return Ok(None);
304    }
305    let Some(open) = arg_type.rfind('[') else {
306        return Ok(None);
307    };
308    let base_type = arg_type[..open].trim();
309    if base_type.is_empty() {
310        return Err(SoldbError::Message(format!(
311            "Invalid array type: {arg_type}"
312        )));
313    }
314
315    let length = &arg_type[open + 1..arg_type.len() - 1];
316    if length.is_empty() {
317        return Ok(Some((base_type, None)));
318    }
319    let length = length.parse::<usize>().map_err(|error| {
320        SoldbError::Message(format!("Invalid fixed array length '{length}': {error}"))
321    })?;
322    Ok(Some((base_type, Some(length))))
323}
324
325fn parse_tuple_type(arg_type: &str) -> Option<Vec<String>> {
326    let inner = arg_type.strip_prefix('(')?.strip_suffix(')')?;
327    split_args(inner)
328}
329
330fn encode_tuple_arg(component_types: &[String], value: &str) -> SoldbResult<EncodedArg> {
331    let values = parse_json_array_arg(value, "tuple")?;
332    if values.len() != component_types.len() {
333        return Err(SoldbError::Message(format!(
334            "Tuple argument expects {} values, got {}",
335            component_types.len(),
336            values.len()
337        )));
338    }
339
340    let encoded_components = component_types
341        .iter()
342        .zip(&values)
343        .map(|(component_type, item)| encode_arg(component_type, &json_value_to_arg_string(item)?))
344        .collect::<SoldbResult<Vec<_>>>()?;
345    let is_dynamic = encoded_components.iter().any(EncodedArg::is_dynamic);
346    let encoded = encode_tuple_payload(encoded_components);
347    if is_dynamic {
348        Ok(EncodedArg::Dynamic(encoded))
349    } else {
350        Ok(EncodedArg::Static(encoded))
351    }
352}
353
354fn encode_array_arg(
355    base_type: &str,
356    fixed_len: Option<usize>,
357    value: &str,
358) -> SoldbResult<EncodedArg> {
359    let values = parse_json_array_arg(value, &format!("{base_type}[]"))?;
360    if let Some(expected) = fixed_len {
361        if values.len() != expected {
362            return Err(SoldbError::Message(format!(
363                "Fixed array argument expects {expected} values, got {}",
364                values.len()
365            )));
366        }
367    }
368
369    let encoded_elements = values
370        .iter()
371        .map(|item| encode_arg(base_type, &json_value_to_arg_string(item)?))
372        .collect::<SoldbResult<Vec<_>>>()?;
373    let has_dynamic_elements = encoded_elements.iter().any(EncodedArg::is_dynamic);
374    let payload = encode_tuple_payload(encoded_elements);
375
376    if fixed_len.is_some() {
377        if has_dynamic_elements {
378            Ok(EncodedArg::Dynamic(payload))
379        } else {
380            Ok(EncodedArg::Static(payload))
381        }
382    } else {
383        let mut encoded = format!("{:064x}", values.len());
384        encoded.push_str(&payload);
385        Ok(EncodedArg::Dynamic(encoded))
386    }
387}
388
389fn parse_json_array_arg(value: &str, label: &str) -> SoldbResult<Vec<Value>> {
390    let values = serde_json::from_str::<Value>(value).map_err(|error| {
391        SoldbError::Message(format!(
392            "Array argument for type '{label}' must be JSON: {error}"
393        ))
394    })?;
395    values.as_array().cloned().ok_or_else(|| {
396        SoldbError::Message(format!(
397            "Array argument for type '{label}' must be a JSON array"
398        ))
399    })
400}
401
402fn json_value_to_arg_string(value: &Value) -> SoldbResult<String> {
403    match value {
404        Value::String(value) => Ok(value.clone()),
405        Value::Number(value) => Ok(value.to_string()),
406        Value::Bool(value) => Ok(value.to_string()),
407        Value::Array(_) => serde_json::to_string(value)
408            .map_err(|error| SoldbError::Message(format!("Invalid array argument: {error}"))),
409        Value::Null => Err(SoldbError::Message(
410            "Null ABI array values are not supported".to_owned(),
411        )),
412        Value::Object(_) => Err(SoldbError::Message(
413            "Object ABI array values are not supported".to_owned(),
414        )),
415    }
416}
417
418fn encode_static_arg(arg_type: &str, value: &str) -> SoldbResult<String> {
419    let arg_type = arg_type.trim();
420    if arg_type.starts_with("uint") {
421        return encode_uint_arg(arg_type, value);
422    }
423    if arg_type.starts_with("int") {
424        return encode_int_arg(arg_type, value);
425    }
426    if arg_type == "address" {
427        return encode_address_arg(value);
428    }
429    if arg_type == "bool" {
430        return encode_bool_arg(value);
431    }
432    if arg_type == "bytes32" {
433        return encode_bytes32_arg(value);
434    }
435    if let Some(width) = arg_type.strip_prefix("bytes") {
436        return encode_fixed_bytes_arg(width, value);
437    }
438
439    Err(SoldbError::Message(format!(
440        "ABI encoding for type '{arg_type}' is not supported"
441    )))
442}
443
444fn encode_uint_arg(arg_type: &str, value: &str) -> SoldbResult<String> {
445    let bits = validate_uint_type(arg_type)?;
446    let encoded = parse_uint_word(value)?;
447    validate_uint_fits_bits(&encoded, bits, value)?;
448    Ok(encoded)
449}
450
451fn encode_int_arg(arg_type: &str, value: &str) -> SoldbResult<String> {
452    validate_int_type(arg_type)?;
453    let parsed = value
454        .parse::<i128>()
455        .map_err(|error| SoldbError::Message(format!("Invalid int value '{value}': {error}")))?;
456    Ok(format!("{:064x}", parsed as u128))
457}
458
459fn encode_address_arg(value: &str) -> SoldbResult<String> {
460    let address = value.trim_start_matches("0x");
461    if address.len() != 40 || !address.bytes().all(|byte| byte.is_ascii_hexdigit()) {
462        return Err(SoldbError::Message(format!(
463            "Invalid address value: {value}"
464        )));
465    }
466    Ok(format!("{address:0>64}").to_ascii_lowercase())
467}
468
469fn encode_bool_arg(value: &str) -> SoldbResult<String> {
470    match value {
471        "true" | "1" => Ok(format!("{:064x}", 1)),
472        "false" | "0" => Ok(format!("{:064x}", 0)),
473        _ => Err(SoldbError::Message(format!("Invalid bool value: {value}"))),
474    }
475}
476
477fn encode_bytes32_arg(value: &str) -> SoldbResult<String> {
478    encode_fixed_bytes_arg("32", value)
479}
480
481fn encode_fixed_bytes_arg(width: &str, value: &str) -> SoldbResult<String> {
482    let width = width.parse::<usize>().map_err(|error| {
483        SoldbError::Message(format!("Invalid fixed bytes width '{width}': {error}"))
484    })?;
485    if !(1..=32).contains(&width) {
486        return Err(SoldbError::Message(format!(
487            "Invalid fixed bytes width: {width}"
488        )));
489    }
490
491    let bytes = parse_hex_bytes(value)?;
492    if bytes.len() != width {
493        return Err(SoldbError::Message(format!(
494            "Invalid bytes{width} value: {value}"
495        )));
496    }
497    let mut encoded = bytes_to_hex(&bytes);
498    encoded.push_str(&"0".repeat((32 - width) * 2));
499    Ok(encoded)
500}
501
502fn encode_dynamic_bytes(bytes: &[u8]) -> String {
503    let mut encoded = format!("{:064x}", bytes.len());
504    encoded.push_str(&bytes_to_hex(bytes));
505    let padding_bytes = (32 - bytes.len() % 32) % 32;
506    encoded.push_str(&"0".repeat(padding_bytes * 2));
507    encoded
508}
509
510fn parse_hex_bytes(value: &str) -> SoldbResult<Vec<u8>> {
511    let bytes = value.trim_start_matches("0x");
512    if !bytes.len().is_multiple_of(2) || !bytes.bytes().all(|byte| byte.is_ascii_hexdigit()) {
513        return Err(SoldbError::Message(format!("Invalid bytes value: {value}")));
514    }
515
516    bytes
517        .as_bytes()
518        .chunks(2)
519        .map(|chunk| {
520            let pair = std::str::from_utf8(chunk)
521                .map_err(|error| SoldbError::Message(format!("Invalid bytes value: {error}")))?;
522            u8::from_str_radix(pair, 16)
523                .map_err(|error| SoldbError::Message(format!("Invalid bytes value: {error}")))
524        })
525        .collect()
526}
527
528fn validate_uint_type(arg_type: &str) -> SoldbResult<u16> {
529    validate_int_bit_width(arg_type.strip_prefix("uint").unwrap_or_default(), "uint")
530}
531
532fn validate_int_type(arg_type: &str) -> SoldbResult<()> {
533    validate_int_bit_width(arg_type.strip_prefix("int").unwrap_or_default(), "int")?;
534    Ok(())
535}
536
537fn validate_int_bit_width(width: &str, label: &str) -> SoldbResult<u16> {
538    if width.is_empty() {
539        return Ok(256);
540    }
541
542    let bits = width.parse::<u16>().map_err(|error| {
543        SoldbError::Message(format!("Invalid {label} bit width '{width}': {error}"))
544    })?;
545    if bits == 0 || bits > 256 || bits % 8 != 0 {
546        return Err(SoldbError::Message(format!(
547            "Invalid {label} bit width: {bits}"
548        )));
549    }
550    Ok(bits)
551}
552
553fn parse_uint_word(value: &str) -> SoldbResult<String> {
554    let value = value.trim();
555    if value.is_empty() {
556        return Err(SoldbError::Message("Invalid uint value: empty".to_owned()));
557    }
558
559    if let Some(hex) = value.strip_prefix("0x") {
560        if hex.is_empty() || hex.len() > 64 || !hex.bytes().all(|byte| byte.is_ascii_hexdigit()) {
561            return Err(SoldbError::Message(format!("Invalid uint value: {value}")));
562        }
563        return Ok(format!("{:0>64}", hex.to_ascii_lowercase()));
564    }
565
566    if !value.bytes().all(|byte| byte.is_ascii_digit()) {
567        return Err(SoldbError::Message(format!("Invalid uint value: {value}")));
568    }
569
570    let mut bytes = [0_u8; 32];
571    for digit in value.bytes().map(|byte| byte - b'0') {
572        let mut carry = u16::from(digit);
573        for byte in bytes.iter_mut().rev() {
574            let next = u16::from(*byte) * 10 + carry;
575            *byte = (next & 0xff) as u8;
576            carry = next >> 8;
577        }
578        if carry != 0 {
579            return Err(SoldbError::Message(format!(
580                "Uint value exceeds 256 bits: {value}"
581            )));
582        }
583    }
584    Ok(bytes_to_hex(&bytes))
585}
586
587fn validate_uint_fits_bits(encoded_word: &str, bits: u16, value: &str) -> SoldbResult<()> {
588    let unused_hex_digits = usize::from(256 - bits) / 4;
589    if encoded_word[..unused_hex_digits]
590        .bytes()
591        .any(|byte| byte != b'0')
592    {
593        return Err(SoldbError::Message(format!(
594            "Uint value does not fit uint{bits}: {value}"
595        )));
596    }
597    Ok(())
598}
599
600fn bytes_to_hex(bytes: &[u8]) -> String {
601    bytes.iter().map(|byte| format!("{byte:02x}")).collect()
602}
603
604fn absorb_block(state: &mut [u64; 25], block: &[u8]) {
605    for (lane, chunk) in block.as_chunks::<8>().0.iter().enumerate() {
606        state[lane] ^= u64::from_le_bytes(*chunk);
607    }
608}
609
610fn keccak_f1600(state: &mut [u64; 25]) {
611    const RHO: [u32; 25] = [
612        0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56,
613        14,
614    ];
615    const RC: [u64; 24] = [
616        0x0000_0000_0000_0001,
617        0x0000_0000_0000_8082,
618        0x8000_0000_0000_808a,
619        0x8000_0000_8000_8000,
620        0x0000_0000_0000_808b,
621        0x0000_0000_8000_0001,
622        0x8000_0000_8000_8081,
623        0x8000_0000_0000_8009,
624        0x0000_0000_0000_008a,
625        0x0000_0000_0000_0088,
626        0x0000_0000_8000_8009,
627        0x0000_0000_8000_000a,
628        0x0000_0000_8000_808b,
629        0x8000_0000_0000_008b,
630        0x8000_0000_0000_8089,
631        0x8000_0000_0000_8003,
632        0x8000_0000_0000_8002,
633        0x8000_0000_0000_0080,
634        0x0000_0000_0000_800a,
635        0x8000_0000_8000_000a,
636        0x8000_0000_8000_8081,
637        0x8000_0000_0000_8080,
638        0x0000_0000_8000_0001,
639        0x8000_0000_8000_8008,
640    ];
641
642    for round_constant in RC {
643        let mut c = [0_u64; 5];
644        for x in 0..5 {
645            c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
646        }
647
648        let mut d = [0_u64; 5];
649        for x in 0..5 {
650            d[x] = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
651        }
652        for y in 0..5 {
653            for x in 0..5 {
654                state[x + 5 * y] ^= d[x];
655            }
656        }
657
658        let mut b = [0_u64; 25];
659        for y in 0..5 {
660            for x in 0..5 {
661                let index = x + 5 * y;
662                b[y + 5 * ((2 * x + 3 * y) % 5)] = state[index].rotate_left(RHO[index]);
663            }
664        }
665
666        for y in 0..5 {
667            for x in 0..5 {
668                state[x + 5 * y] =
669                    b[x + 5 * y] ^ ((!b[((x + 1) % 5) + 5 * y]) & b[((x + 2) % 5) + 5 * y]);
670            }
671        }
672
673        state[0] ^= round_constant;
674    }
675}
676
677#[cfg(test)]
678mod tests {
679    use super::{
680        canonical_abi_input_type, encode_abi_arguments, encode_function_call, function_selector,
681        keccak256, match_abi_types, match_single_type, parse_signature, parse_tuple_arg, AbiInput,
682    };
683    use serde_json::json;
684
685    #[test]
686    fn parses_nested_function_signatures() {
687        let signature = parse_signature("foo((uint256,address),uint8[])").expect("valid");
688        assert_eq!(signature.name, "foo");
689        assert_eq!(signature.arg_types, ["(uint256,address)", "uint8[]"]);
690
691        let noop = parse_signature("noop()").expect("valid");
692        assert_eq!(noop.name, "noop");
693        assert!(noop.arg_types.is_empty());
694
695        assert!(parse_signature("not a signature").is_none());
696        assert!(parse_signature("broken(uint256").is_none());
697    }
698
699    #[test]
700    fn matches_tuple_and_array_types() {
701        let parsed = vec![
702            "uint256".to_owned(),
703            "(address,uint256)".to_owned(),
704            "uint256[]".to_owned(),
705        ];
706        let abi = vec![
707            "uint256".to_owned(),
708            "tuple".to_owned(),
709            "uint256[]".to_owned(),
710        ];
711
712        assert!(match_abi_types(&parsed, &abi));
713        assert!(match_single_type("tuple(uint256)", "tuple"));
714        assert!(!match_single_type("uint8[]", "uint256[]"));
715        assert!(!match_abi_types(&["uint256".to_owned()], &abi));
716    }
717
718    #[test]
719    fn parses_tuple_arguments_recursively() {
720        let abi_input: AbiInput = serde_json::from_value(json!({
721            "type": "tuple",
722            "components": [
723                {"type": "uint256"},
724                {"type": "tuple", "components": [{"type": "address"}]},
725                {"type": "tuple[]", "components": [{"type": "uint256"}]}
726            ]
727        }))
728        .expect("abi input");
729
730        let parsed =
731            parse_tuple_arg(&json!([1, ["0xabc"], [[2], [3]]]), &abi_input).expect("tuple parses");
732        assert_eq!(parsed, json!([1, ["0xabc"], [[2], [3]]]));
733
734        assert!(parse_tuple_arg(&json!("bad"), &abi_input).is_err());
735    }
736
737    #[test]
738    fn canonicalizes_tuple_abi_input_types() {
739        let abi_input: AbiInput = serde_json::from_value(json!({
740            "type": "tuple[]",
741            "components": [
742                {"type": "uint256"},
743                {"type": "tuple", "components": [{"type": "address"}]}
744            ]
745        }))
746        .expect("abi input");
747
748        assert_eq!(
749            canonical_abi_input_type(&abi_input),
750            "(uint256,(address))[]"
751        );
752    }
753
754    #[test]
755    fn computes_ethereum_keccak256_selectors() {
756        assert_eq!(
757            hex(keccak256(b"").as_ref()),
758            "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
759        );
760        assert_eq!(
761            hex(function_selector("transfer(address,uint256)")
762                .expect("selector")
763                .as_ref()),
764            "a9059cbb"
765        );
766        assert_eq!(
767            hex(function_selector("increment(uint256)")
768                .expect("selector")
769                .as_ref()),
770            "7cf5dab0"
771        );
772    }
773
774    #[test]
775    fn hashes_inputs_spanning_the_keccak_rate() {
776        // The absorb loop consumes 136-byte blocks, so the cases that matter sit either
777        // side of that boundary. Nothing else here reaches it: the existing vectors are a
778        // few dozen bytes at most, which leaves the multi-block path untested.
779        // Expected digests come from `cast keccak`.
780        fn pattern(len: usize) -> Vec<u8> {
781            (0..len)
782                .map(|index| u8::try_from((index * 7 + 3) % 256).expect("value fits a byte"))
783                .collect()
784        }
785
786        for (len, expected) in [
787            (
788                135,
789                "00ef96af9cf4b24c7f269d922294444a197d0a33638c2e56634c57e892103a8f",
790            ),
791            (
792                136,
793                "742061bcad767ed4c4f5883b1dcb1aad11afdcc140dc469d953759b127b9f9ed",
794            ),
795            (
796                137,
797                "e3371f61e770abf254c34239c3b0099ad90594507415bc81dd0a10b9692bbf2a",
798            ),
799            (
800                272,
801                "ac141fd7b0a0ffcd2e967254d508da3ec616596493c36fa304425647d90e6de5",
802            ),
803            (
804                300,
805                "fa75f2293be9f9a14dcdeeff53f7b91ff6a2b1331b13886e69077ab1cf8252a9",
806            ),
807        ] {
808            assert_eq!(
809                hex(keccak256(&pattern(len)).as_ref()),
810                expected,
811                "len {len}"
812            );
813        }
814    }
815
816    #[test]
817    fn encodes_static_function_calls() {
818        assert_eq!(
819            encode_function_call("increment(uint256)", &["4".to_owned()]).expect("calldata"),
820            "0x7cf5dab00000000000000000000000000000000000000000000000000000000000000004"
821        );
822        assert_eq!(
823            encode_function_call(
824                "transfer(address,uint256)",
825                &[
826                    "0x0000000000000000000000000000000000000002".to_owned(),
827                    "0x2a".to_owned(),
828                ],
829            )
830            .expect("calldata"),
831            "0xa9059cbb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002a"
832        );
833        assert_eq!(
834            encode_function_call(
835                "set(uint256)",
836                &["1606938044258990275541962092341162602522202993782792835301376".to_owned()],
837            )
838            .expect("calldata"),
839            "0x60fe47b10000000000000100000000000000000000000000000000000000000000000000"
840        );
841    }
842
843    #[test]
844    fn encodes_dynamic_function_calls() {
845        let selector = hex(function_selector("set(string)").expect("selector").as_ref());
846        assert_eq!(
847            encode_function_call("set(string)", &["hi".to_owned()]).expect("calldata"),
848            format!(
849                "0x{selector}\
850                0000000000000000000000000000000000000000000000000000000000000020\
851                0000000000000000000000000000000000000000000000000000000000000002\
852                6869{}",
853                "0".repeat(60)
854            )
855        );
856
857        let selector = hex(function_selector("processOrder(uint256,string,string)")
858            .expect("selector")
859            .as_ref());
860        assert_eq!(
861            encode_function_call(
862                "processOrder(uint256,string,string)",
863                &[
864                    "10".to_owned(),
865                    "express".to_owned(),
866                    "electronic".to_owned()
867                ],
868            )
869            .expect("calldata"),
870            format!(
871                "0x{selector}\
872                000000000000000000000000000000000000000000000000000000000000000a\
873                0000000000000000000000000000000000000000000000000000000000000060\
874                00000000000000000000000000000000000000000000000000000000000000a0\
875                0000000000000000000000000000000000000000000000000000000000000007\
876                65787072657373{}\
877                000000000000000000000000000000000000000000000000000000000000000a\
878                656c656374726f6e6963{}",
879                "0".repeat(50),
880                "0".repeat(44)
881            )
882        );
883
884        let selector = hex(function_selector("set(bytes)").expect("selector").as_ref());
885        assert_eq!(
886            encode_function_call("set(bytes)", &["0xabcd".to_owned()]).expect("calldata"),
887            format!(
888                "0x{selector}\
889                0000000000000000000000000000000000000000000000000000000000000020\
890                0000000000000000000000000000000000000000000000000000000000000002\
891                abcd{}",
892                "0".repeat(60)
893            )
894        );
895    }
896
897    #[test]
898    fn encodes_fixed_bytes_function_calls() {
899        let selector = hex(function_selector("set(bytes2)").expect("selector").as_ref());
900        assert_eq!(
901            encode_function_call("set(bytes2)", &["0xabcd".to_owned()]).expect("calldata"),
902            format!("0x{selector}abcd{}", "0".repeat(60))
903        );
904    }
905
906    #[test]
907    fn encodes_abi_argument_payload_without_selector() {
908        let arg_types = vec!["uint256".to_owned(), "string".to_owned()];
909        let args = vec!["7".to_owned(), "hi".to_owned()];
910
911        assert_eq!(
912            encode_abi_arguments(&arg_types, &args).expect("payload"),
913            format!(
914                "\
915                0000000000000000000000000000000000000000000000000000000000000007\
916                0000000000000000000000000000000000000000000000000000000000000040\
917                0000000000000000000000000000000000000000000000000000000000000002\
918                6869{}",
919                "0".repeat(60)
920            )
921        );
922    }
923
924    #[test]
925    fn encodes_dynamic_array_function_calls() {
926        assert_eq!(
927            encode_function_call("set(uint256[])", &["[1,2,3]".to_owned()]).expect("calldata"),
928            "0x6ea9bfc500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"
929        );
930
931        assert_eq!(
932            encode_function_call("set(string[])", &["[\"a\",\"bb\"]".to_owned()])
933                .expect("calldata"),
934            "0x52e66db800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026262000000000000000000000000000000000000000000000000000000000000"
935        );
936
937        assert_eq!(
938            encode_function_call(
939                "mix(uint256[],string)",
940                &["[1,2]".to_owned(), "ok".to_owned()],
941            )
942            .expect("calldata"),
943            "0xb613ef8d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000026f6b000000000000000000000000000000000000000000000000000000000000"
944        );
945    }
946
947    #[test]
948    fn encodes_fixed_array_and_tuple_function_calls() {
949        assert_eq!(
950            encode_function_call("set(uint256[3])", &["[1,2,3]".to_owned()]).expect("calldata"),
951            "0xcf1d72d1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"
952        );
953
954        assert_eq!(
955            encode_function_call(
956                "mix(uint256[2],string)",
957                &["[1,2]".to_owned(), "ok".to_owned()],
958            )
959            .expect("calldata"),
960            "0x48ec1f2c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000026f6b000000000000000000000000000000000000000000000000000000000000"
961        );
962
963        assert_eq!(
964            encode_function_call(
965                "set((uint256,address))",
966                &["[5,\"0x0000000000000000000000000000000000000002\"]".to_owned()],
967            )
968            .expect("calldata"),
969            "0x0193c8b800000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000002"
970        );
971
972        assert_eq!(
973            encode_function_call("set((uint256,string))", &["[5,\"hi\"]".to_owned()])
974                .expect("calldata"),
975            "0xa16fc8d400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000026869000000000000000000000000000000000000000000000000000000000000"
976        );
977
978        assert_eq!(
979            encode_function_call("set(string[2])", &["[\"a\",\"bb\"]".to_owned()])
980                .expect("calldata"),
981            "0x74d379540000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026262000000000000000000000000000000000000000000000000000000000000"
982        );
983    }
984
985    #[test]
986    fn rejects_unsupported_or_invalid_encoding() {
987        assert!(encode_function_call("set(uint256)", &[]).is_err());
988        assert!(encode_function_call("set(uint8)", &["256".to_owned()]).is_err());
989        assert!(encode_function_call("set(address)", &["0x1".to_owned()]).is_err());
990        assert!(encode_function_call("set(bool)", &["maybe".to_owned()]).is_err());
991        assert!(encode_function_call("set(bytes)", &["0xabc".to_owned()]).is_err());
992        assert!(encode_function_call("set(bytes)", &["0xzz".to_owned()]).is_err());
993        assert!(encode_function_call("set(bytes2)", &["0xab".to_owned()]).is_err());
994        assert!(encode_function_call("set(bytes33)", &["0xab".to_owned()]).is_err());
995        assert!(encode_function_call("set(uint256[])", &["1,2".to_owned()]).is_err());
996        assert!(encode_function_call("set(address[])", &["[\"0x1\"]".to_owned()]).is_err());
997        assert!(encode_function_call("set(uint256[2])", &["[1]".to_owned()]).is_err());
998        assert!(encode_function_call("set((uint256,bool))", &["[1]".to_owned()]).is_err());
999    }
1000
1001    fn hex(bytes: &[u8]) -> String {
1002        bytes.iter().map(|byte| format!("{byte:02x}")).collect()
1003    }
1004}