Skip to main content

ethrex_l2_sdk/
calldata.rs

1use ethrex_common::Bytes;
2use ethrex_common::H256;
3use ethrex_common::utils::{decode_hex, keccak};
4use ethrex_common::{Address, H32, U256};
5use ethrex_l2_common::calldata::Value;
6use ethrex_rpc::clients::EthClientError;
7use ethrex_rpc::clients::eth::errors::CalldataEncodeError;
8
9use crate::address_to_word;
10
11#[derive(Debug, thiserror::Error)]
12pub enum CalldataDecodeError {
13    #[error("Failed to parse function signature: {0}")]
14    ParseError(String),
15    #[error("Invalid calldata. Tried to read more bytes than there are.")]
16    OutOfBounds,
17    #[error("Internal Calldata decoding error. This is most likely a bug")]
18    InternalError,
19}
20
21pub fn parse_signature(signature: &str) -> Result<(String, Vec<String>), CalldataEncodeError> {
22    let sig = signature.trim().trim_start_matches("function ");
23    let (name, params) = sig
24        .split_once('(')
25        .ok_or(CalldataEncodeError::ParseError(signature.to_owned()))?;
26    let params = params.rsplit_once(')').map_or(params, |(left, _)| left);
27
28    // We use this to only keep track of top level tuples
29    // "address,(uint256,uint256)" -> "address" and "(uint256,uint256)"
30    // "address,(unit256,(uint256,uint256))" -> "address" and "(unit256,(uint256,uint256))"
31    let mut splitted_params = Vec::new();
32    let mut current_param = String::new();
33    let mut parenthesis_depth = 0;
34
35    for ch in params.chars() {
36        match ch {
37            '(' => {
38                parenthesis_depth += 1;
39                current_param.push(ch);
40            }
41            ')' => {
42                parenthesis_depth -= 1;
43                current_param.push(ch);
44            }
45            ',' if parenthesis_depth == 0 => {
46                if !current_param.is_empty() {
47                    splitted_params.push(current_param.trim().to_string());
48                    current_param = String::new();
49                }
50            }
51            _ => current_param.push(ch),
52        }
53    }
54
55    // push the last param if it exists
56    if !current_param.is_empty() {
57        splitted_params.push(current_param.trim().to_string());
58    }
59
60    Ok((name.to_string(), splitted_params))
61}
62
63#[doc(hidden)]
64/// Computes the 4-byte function selector from a function name and parameters.
65///
66/// Exposed for testing - not part of the stable public API.
67pub fn compute_function_selector(
68    name: &str,
69    params: &[String],
70) -> Result<H32, CalldataEncodeError> {
71    let normalized_signature = format!("{name}({})", params.join(","));
72    let hash = keccak(normalized_signature.as_bytes());
73
74    Ok(H32::from(&hash[..4].try_into().map_err(|_| {
75        CalldataEncodeError::ParseError(name.to_owned())
76    })?))
77}
78
79pub fn encode_calldata(signature: &str, values: &[Value]) -> Result<Vec<u8>, CalldataEncodeError> {
80    let (name, params) = parse_signature(signature)?;
81
82    // Checks if params = [""]
83    // that case happen when we have a function selector as follows: function name()
84    let mut params = params;
85    if params.is_empty() {
86        params = vec![];
87    }
88
89    if params.len() != values.len() {
90        return Err(CalldataEncodeError::WrongArgumentLength(
91            signature.to_owned(),
92        ));
93    }
94
95    let function_selector = compute_function_selector(&name, &params)?;
96    let calldata = encode_tuple(values)?;
97    let mut with_selector = function_selector.as_bytes().to_vec();
98
99    with_selector.extend_from_slice(&calldata);
100
101    Ok(with_selector)
102}
103
104pub fn decode_calldata(signature: &str, data: Bytes) -> Result<Vec<Value>, CalldataDecodeError> {
105    let (_, params) =
106        parse_signature(signature).map_err(|e| CalldataDecodeError::ParseError(e.to_string()))?;
107    let mut decoder = DecodeHelper::new(&data);
108    let datatype = DataType::Tuple(
109        params
110            .iter()
111            .map(|v| DataType::parse(v))
112            .collect::<Result<Vec<_>, _>>()?,
113    );
114    match datatype.decode(&mut decoder)? {
115        Value::Tuple(values) => Ok(values),
116        _ => Err(CalldataDecodeError::InternalError),
117    }
118}
119
120struct DecodeHelper<'a> {
121    buf: &'a [u8],
122    index: usize,
123}
124
125const SELECTOR_SIZE: usize = 4;
126
127impl<'a> DecodeHelper<'a> {
128    fn new(buf: &'a [u8]) -> Self {
129        DecodeHelper {
130            buf,
131            index: SELECTOR_SIZE,
132        }
133    }
134    fn consume(&mut self, n: usize) -> Result<&'a [u8], CalldataDecodeError> {
135        let data = self
136            .buf
137            .get(self.index..self.index + n)
138            .ok_or(CalldataDecodeError::OutOfBounds)?;
139        self.index += n;
140        Ok(data)
141    }
142    fn consume_u256(&mut self) -> Result<U256, CalldataDecodeError> {
143        Ok(U256::from_big_endian(self.consume(32)?))
144    }
145    fn start_reading_at(&self, offset: usize) -> Result<Self, CalldataDecodeError> {
146        let data = self
147            .buf
148            .get(self.index + offset..)
149            .ok_or(CalldataDecodeError::OutOfBounds)?;
150        Ok(DecodeHelper {
151            buf: data,
152            index: 0,
153        })
154    }
155}
156
157#[derive(Clone, Debug)]
158enum DataType {
159    Array(Box<DataType>),
160    FixedArray(usize, Box<DataType>),
161    Tuple(Vec<DataType>),
162    Bytes,
163    FixedBytes(usize),
164    Address,
165    Bool,
166    Uint,
167    Int,
168}
169
170impl DataType {
171    fn parse(param: &str) -> Result<Self, CalldataDecodeError> {
172        Ok(match param {
173            _ if param.ends_with("[]") => {
174                let inner = param
175                    .strip_suffix("[]")
176                    .ok_or(CalldataDecodeError::InternalError)?;
177                DataType::Array(Box::new(DataType::parse(inner)?))
178            }
179            _ if param.ends_with("]") => {
180                let mut n = String::new();
181                let mut iter = param.chars().rev().skip(1);
182                for c in iter.by_ref() {
183                    if c.is_ascii_digit() {
184                        n.insert(0, c);
185                    } else {
186                        if c != '[' {
187                            return Err(CalldataDecodeError::ParseError(format!(
188                                "expected ] but found {c}"
189                            )));
190                        }
191                        break;
192                    }
193                }
194                iter.next();
195                let inner: String = iter.collect::<String>().chars().rev().collect();
196                let n: usize = n.parse().map_err(|_| CalldataDecodeError::OutOfBounds)?;
197                DataType::FixedArray(n, Box::new(DataType::parse(&inner)?))
198            }
199            _ if param.ends_with(")") => {
200                let (_, inner) = parse_signature(param)
201                    .map_err(|e| CalldataDecodeError::ParseError(e.to_string()))?;
202                DataType::Tuple(
203                    inner
204                        .iter()
205                        .map(|v| DataType::parse(v))
206                        .collect::<Result<Vec<_>, _>>()?,
207                )
208            }
209            "address" => DataType::Address,
210            "bool" => DataType::Bool,
211            "bytes" => DataType::Bytes,
212            _ if param.starts_with("bytes") => {
213                let n = param
214                    .trim_start_matches("bytes")
215                    .parse()
216                    .map_err(|_| CalldataDecodeError::ParseError("invalid bytesN".to_string()))?;
217                DataType::FixedBytes(n)
218            }
219            _ if param.starts_with("uint") => DataType::Uint,
220            _ if param.starts_with("int") => DataType::Int,
221            _ => {
222                return Err(CalldataDecodeError::ParseError(format!(
223                    "unknown type {param}"
224                )));
225            }
226        })
227    }
228    fn is_dynamic(&self) -> bool {
229        match self {
230            DataType::Array(_) => true,
231            DataType::Bytes => true,
232            DataType::FixedArray(_, inner) => inner.is_dynamic(),
233            DataType::Tuple(inner) => inner.iter().any(|t| t.is_dynamic()),
234            _ => false,
235        }
236    }
237    fn decode(&self, data: &mut DecodeHelper) -> Result<Value, CalldataDecodeError> {
238        Ok(match self {
239            DataType::Uint => Value::Uint(data.consume_u256()?),
240            DataType::Int => Value::Int(data.consume_u256()?),
241            DataType::Address => {
242                data.consume(32 - 20)?;
243                Value::Address(Address::from_slice(data.consume(20)?))
244            }
245            DataType::Bool => Value::Bool(!data.consume_u256()?.is_zero()),
246            DataType::FixedBytes(n) => Value::FixedBytes(
247                data.consume(32)?
248                    .get(0..*n)
249                    .ok_or(CalldataDecodeError::OutOfBounds)?
250                    .to_vec()
251                    .into(),
252            ),
253            DataType::Bytes => {
254                let n: usize = data
255                    .consume_u256()?
256                    .try_into()
257                    .map_err(|_| CalldataDecodeError::OutOfBounds)?;
258                let size = if n.is_multiple_of(32) {
259                    n
260                } else {
261                    n.next_multiple_of(32)
262                };
263                Value::Bytes(
264                    data.consume(size)?
265                        .get(0..n)
266                        .ok_or(CalldataDecodeError::OutOfBounds)?
267                        .to_vec()
268                        .into(),
269                )
270            }
271            DataType::FixedArray(n, inner_type) => {
272                let inner_type = *inner_type.clone();
273                let value = DataType::Tuple(vec![inner_type; *n]).decode(data)?;
274                match value {
275                    Value::Tuple(inner) => Value::FixedArray(inner),
276                    _ => return Err(CalldataDecodeError::InternalError),
277                }
278            }
279            DataType::Tuple(inner_types) => {
280                let mut values = Vec::new();
281                let start_reader = data.start_reading_at(0)?;
282                for inner_type in inner_types {
283                    if inner_type.is_dynamic() {
284                        let offset: usize = data
285                            .consume_u256()?
286                            .try_into()
287                            .map_err(|_| CalldataDecodeError::OutOfBounds)?;
288                        values
289                            .push(inner_type.decode(&mut start_reader.start_reading_at(offset)?)?);
290                    } else {
291                        values.push(inner_type.decode(data)?);
292                    }
293                }
294                Value::Tuple(values)
295            }
296            DataType::Array(inner_type) => {
297                let n: usize = data
298                    .consume_u256()?
299                    .try_into()
300                    .map_err(|_| CalldataDecodeError::OutOfBounds)?;
301                let mut values = Vec::new();
302                for _ in 0..n {
303                    values.push(inner_type.decode(data)?);
304                }
305                Value::Array(values)
306            }
307        })
308    }
309}
310
311// This is the main entrypoint for ABI encoding solidity function arguments, as the list of arguments themselves are
312// considered a tuple. Before going through this function, read the solidity ABI spec first
313// https://docs.soliditylang.org/en/develop/abi-spec.html.
314// The encoding of a tuple consists of two parts: a static and a dynamic one (what the spec calls the head and tail of the encoding).
315// The dynamic part always follows at the end of the static one.
316// Arguments are encoded in order. If the argument is static, it is encoded in place, i.e, there's no dynamic part.
317// If the argument is dynamic, only its offset to the dynamic part is recorded on the static sector.
318pub fn encode_tuple(values: &[Value]) -> Result<Vec<u8>, CalldataEncodeError> {
319    let mut current_offset = 0;
320    let mut current_dynamic_offset = 0;
321    for value in values {
322        current_dynamic_offset += static_offset_value(value);
323    }
324
325    let mut ret = vec![0; current_dynamic_offset];
326
327    for value in values {
328        match value {
329            Value::Address(h160) => {
330                write_u256(&mut ret, address_to_word(*h160), current_offset)?;
331            }
332            Value::Uint(u256) => {
333                write_u256(&mut ret, *u256, current_offset)?;
334            }
335            Value::Int(u256) => {
336                write_u256(&mut ret, *u256, current_offset)?;
337            }
338            Value::Bool(boolean) => {
339                write_u256(&mut ret, U256::from(u8::from(*boolean)), current_offset)?;
340            }
341            Value::Bytes(bytes) => {
342                write_u256(&mut ret, U256::from(current_dynamic_offset), current_offset)?;
343
344                let bytes_encoding = encode_bytes(bytes);
345                ret.extend_from_slice(&bytes_encoding);
346                current_dynamic_offset += bytes_encoding.len();
347            }
348            Value::String(string_value) => {
349                write_u256(&mut ret, U256::from(current_dynamic_offset), current_offset)?;
350
351                let utf8_encoded = Bytes::copy_from_slice(string_value.as_bytes());
352                let bytes_encoding = encode_bytes(&utf8_encoded);
353                ret.extend_from_slice(&bytes_encoding);
354                current_dynamic_offset += bytes_encoding.len();
355            }
356            Value::Array(array_values) => {
357                write_u256(&mut ret, U256::from(current_dynamic_offset), current_offset)?;
358
359                let array_encoding = encode_array(array_values)?;
360                ret.extend_from_slice(&array_encoding);
361                current_dynamic_offset += array_encoding.len();
362            }
363            Value::Tuple(tuple_values) => {
364                if !is_dynamic(value) {
365                    let tuple_encoding = encode_tuple(tuple_values)?;
366                    copy_into(
367                        &mut ret,
368                        &tuple_encoding,
369                        current_offset,
370                        tuple_encoding.len(),
371                    )?;
372                } else {
373                    write_u256(&mut ret, U256::from(current_dynamic_offset), current_offset)?;
374
375                    let tuple_encoding = encode_tuple(tuple_values)?;
376                    ret.extend_from_slice(&tuple_encoding);
377                    current_dynamic_offset += tuple_encoding.len();
378                }
379            }
380            Value::FixedArray(fixed_array_values) => {
381                if !is_dynamic(value) {
382                    let fixed_array_encoding = encode_tuple(fixed_array_values)?;
383                    copy_into(
384                        &mut ret,
385                        &fixed_array_encoding,
386                        current_offset,
387                        fixed_array_encoding.len(),
388                    )?;
389                } else {
390                    write_u256(&mut ret, U256::from(current_dynamic_offset), current_offset)?;
391
392                    let tuple_encoding = encode_tuple(fixed_array_values)?;
393                    ret.extend_from_slice(&tuple_encoding);
394                    current_dynamic_offset += tuple_encoding.len();
395                }
396            }
397            Value::FixedBytes(bytes) => {
398                let mut bytes = bytes.to_vec();
399                bytes.resize(32, 0);
400                copy_into(&mut ret, &bytes, current_offset, 32)?;
401            }
402        }
403
404        current_offset += static_offset_value(value);
405    }
406
407    Ok(ret)
408}
409
410fn write_u256(values: &mut [u8], number: U256, offset: usize) -> Result<(), CalldataEncodeError> {
411    let to_copy = number.to_big_endian();
412    copy_into(values, &to_copy, offset, 32)?;
413
414    Ok(())
415}
416
417// Returns the size that the value occupies in the static sector of the abi encoding.
418// For dynamic types, this is always 32 (the offset to the dynamic sector).
419// For static types, it's 32 unless the value is a static tuple or a fixed array, in which case
420// it's the sum of the sizes of their elements.
421fn static_offset_value(value: &Value) -> usize {
422    let mut ret = 0;
423
424    match value {
425        Value::Address(_)
426        | Value::Uint(_)
427        | Value::Int(_)
428        | Value::Bool(_)
429        | Value::Bytes(_)
430        | Value::String(_)
431        | Value::Array(_)
432        | Value::FixedBytes(_) => ret += 32,
433        Value::Tuple(vec) => {
434            if is_dynamic(value) {
435                ret += 32;
436            } else {
437                for element in vec {
438                    // Here every element is guaranteed to be static, otherwise we would not be
439                    // in the `else` branch of the `if` statement.
440                    ret += static_offset_value(element);
441                }
442            }
443        }
444        Value::FixedArray(vec) => {
445            if is_dynamic(value) {
446                ret += 32;
447            } else {
448                for element in vec {
449                    // Here every element is guaranteed to be static (and of the same type), otherwise we would not be
450                    // in the `else` branch of the `if` statement.
451                    ret += static_offset_value(element);
452                }
453            }
454        }
455    }
456
457    ret
458}
459
460fn is_dynamic(value: &Value) -> bool {
461    match value {
462        Value::Bytes(_) | Value::String(_) | Value::Array(_) => true,
463        Value::Tuple(vec) => vec.iter().any(is_dynamic),
464        Value::FixedArray(vec) => {
465            if let Some(first_elem) = vec.first() {
466                is_dynamic(first_elem)
467            } else {
468                false
469            }
470        }
471        _ => false,
472    }
473}
474
475fn encode_array(values: &[Value]) -> Result<Vec<u8>, CalldataEncodeError> {
476    let mut ret = vec![];
477    let to_copy = U256::from(values.len()).to_big_endian();
478    ret.extend_from_slice(&to_copy);
479
480    let tuple_encoding = encode_tuple(values)?;
481    ret.extend_from_slice(&tuple_encoding);
482
483    Ok(ret)
484}
485
486fn encode_bytes(values: &Bytes) -> Vec<u8> {
487    let mut ret = vec![];
488
489    // the bytes has to be padded to 32 bytes
490    let padding = 32 - (values.len() % 32);
491    let mut padded_bytes = values.to_vec();
492    if padding != 32 {
493        padded_bytes.extend_from_slice(&vec![0; padding]);
494    }
495
496    let to_copy = U256::from(values.len()).to_big_endian(); // we write the length without padding
497
498    ret.extend_from_slice(&to_copy);
499    ret.extend_from_slice(&padded_bytes);
500
501    ret
502}
503
504fn copy_into(
505    values: &mut [u8],
506    to_copy: &[u8],
507    offset: usize,
508    size: usize,
509) -> Result<(), CalldataEncodeError> {
510    let to_copy_slice = to_copy
511        .get(..size)
512        .ok_or(CalldataEncodeError::InternalError)?;
513
514    values
515        .get_mut(offset..(size + offset))
516        .ok_or(CalldataEncodeError::InternalError)?
517        .copy_from_slice(to_copy_slice);
518
519    Ok(())
520}
521
522#[allow(clippy::indexing_slicing)]
523pub fn from_hex_string_to_h256_array(hex_string: &str) -> Result<Vec<H256>, EthClientError> {
524    let bytes = decode_hex(hex_string)
525        .map_err(|_| EthClientError::Custom("Invalid hex string".to_owned()))?;
526
527    // The ABI encoding for dynamic arrays is:
528    // 1. Offset to data (32 bytes)
529    // 2. Length of array (32 bytes)
530    // 3. Array elements (each 32 bytes)
531    if bytes.len() < 64 {
532        return Err(EthClientError::Custom("Response too short".to_owned()));
533    }
534
535    // Get the offset (should be 0x20 for simple arrays)
536    let offset = usize::try_from(U256::from_big_endian(&bytes[0..32]))
537        .map_err(|_| EthClientError::Custom("ABI offset overflows usize".to_owned()))?;
538
539    // Get the length of the array
540    let length = usize::try_from(U256::from_big_endian(&bytes[offset..offset + 32]))
541        .map_err(|_| EthClientError::Custom("ABI array length overflows usize".to_owned()))?;
542
543    // Calculate the start of the array data
544    let data_start = offset + 32;
545    let data_end = data_start + (length * 32);
546
547    if data_end > bytes.len() {
548        return Err(EthClientError::Custom("Invalid array length".to_owned()));
549    }
550
551    // Convert the slice directly to H256 array
552    bytes[data_start..data_end]
553        .chunks_exact(32)
554        .map(|chunk| Ok(H256::from_slice(chunk)))
555        .collect()
556}