cherry_evm_decode/
lib.rs

1use std::sync::Arc;
2
3use alloy_dyn_abi::{DynSolCall, DynSolEvent, DynSolType, DynSolValue, Specifier};
4use alloy_primitives::{I256, U256};
5use anyhow::{anyhow, Context, Result};
6use arrow::{
7    array::{builder, Array, ArrowPrimitiveType, BinaryArray, ListArray, RecordBatch, StructArray},
8    buffer::{NullBuffer, OffsetBuffer},
9    datatypes::{
10        DataType, Field, Fields, Int16Type, Int32Type, Int64Type, Int8Type, Schema, UInt16Type,
11        UInt32Type, UInt64Type, UInt8Type,
12    },
13};
14
15/// Returns topic0 based on given event signature
16pub fn signature_to_topic0(signature: &str) -> Result<[u8; 32]> {
17    let event = alloy_json_abi::Event::parse(signature).context("parse event signature")?;
18    Ok(event.selector().into())
19}
20
21/// Decodes given call input data in arrow format to arrow format.
22/// Output Arrow schema is auto generated based on the function signature.
23/// Handles any level of nesting with Lists/Structs.
24///
25/// Writes `null` for data rows that fail to decode if `allow_decode_fail` is set to `true`.
26/// Errors when a row fails to decode if `allow_decode_fail` is set to `false`.
27pub fn decode_call_inputs(
28    signature: &str,
29    data: &BinaryArray,
30    allow_decode_fail: bool,
31) -> Result<RecordBatch> {
32    decode_call_impl::<true>(signature, data, allow_decode_fail)
33}
34
35/// Decodes given call output data in arrow format to arrow format.
36/// Output Arrow schema is auto generated based on the function signature.
37/// Handles any level of nesting with Lists/Structs.
38///
39/// Writes `null` for data rows that fail to decode if `allow_decode_fail` is set to `true`.
40/// Errors when a row fails to decode if `allow_decode_fail` is set to `false`.
41pub fn decode_call_outputs(
42    signature: &str,
43    data: &BinaryArray,
44    allow_decode_fail: bool,
45) -> Result<RecordBatch> {
46    decode_call_impl::<false>(signature, data, allow_decode_fail)
47}
48
49// IS_INPUT: true means we are decoding inputs
50// false means we are decoding outputs
51fn decode_call_impl<const IS_INPUT: bool>(
52    signature: &str,
53    data: &BinaryArray,
54    allow_decode_fail: bool,
55) -> Result<RecordBatch> {
56    let (call, resolved) = resolve_function_signature(signature)?;
57
58    let schema = function_signature_to_arrow_schemas_impl(&call, &resolved)
59        .context("convert event signature to arrow schema")?;
60    let schema = if IS_INPUT { schema.0 } else { schema.1 };
61
62    let mut arrays: Vec<Arc<dyn Array + 'static>> = Vec::with_capacity(schema.fields().len());
63
64    let mut decoded = Vec::<Option<DynSolValue>>::with_capacity(data.len());
65
66    for blob in data.iter() {
67        match blob {
68            Some(blob) => {
69                let decode_res = if IS_INPUT {
70                    resolved.abi_decode_input(blob, false)
71                } else {
72                    resolved.abi_decode_output(blob, false)
73                };
74                match decode_res {
75                    Ok(data) => decoded.push(Some(DynSolValue::Tuple(data))),
76                    Err(e) if allow_decode_fail => {
77                        log::debug!("failed to decode function call data: {}", e);
78                        decoded.push(None);
79                    }
80                    Err(e) => {
81                        return Err(anyhow!("failed to decode function call data: {}", e));
82                    }
83                }
84            }
85            None => decoded.push(None),
86        }
87    }
88
89    let sol_type = if IS_INPUT {
90        DynSolType::Tuple(resolved.types().to_vec())
91    } else {
92        DynSolType::Tuple(resolved.returns().types().to_vec())
93    };
94
95    let array = to_arrow(&sol_type, decoded).context("map params to arrow")?;
96    match array.data_type() {
97        DataType::Struct(_) => {
98            let arr = array.as_any().downcast_ref::<StructArray>().unwrap();
99
100            for f in arr.columns().iter() {
101                arrays.push(f.clone());
102            }
103        }
104        _ => unreachable!(),
105    }
106
107    RecordBatch::try_new(Arc::new(schema), arrays).context("construct arrow batch")
108}
109
110/// Returns (input schema, output schema)
111pub fn function_signature_to_arrow_schemas(signature: &str) -> Result<(Schema, Schema)> {
112    let (func, resolved) = resolve_function_signature(signature)?;
113    function_signature_to_arrow_schemas_impl(&func, &resolved)
114}
115
116fn function_signature_to_arrow_schemas_impl(
117    func: &alloy_json_abi::Function,
118    call: &DynSolCall,
119) -> Result<(Schema, Schema)> {
120    let mut input_fields = Vec::with_capacity(call.types().len());
121    let mut output_fields = Vec::with_capacity(call.returns().types().len());
122
123    for (i, (sol_t, param)) in call.types().iter().zip(func.inputs.iter()).enumerate() {
124        let dtype = to_arrow_dtype(sol_t).context("map to arrow type")?;
125        let name = if param.name() == "" {
126            format!("param{}", i)
127        } else {
128            param.name().to_owned()
129        };
130        input_fields.push(Arc::new(Field::new(name, dtype, true)));
131    }
132
133    for (i, (sol_t, param)) in call
134        .returns()
135        .types()
136        .iter()
137        .zip(func.outputs.iter())
138        .enumerate()
139    {
140        let dtype = to_arrow_dtype(sol_t).context("map to arrow type")?;
141        let name = if param.name() == "" {
142            format!("param{}", i)
143        } else {
144            param.name().to_owned()
145        };
146        output_fields.push(Arc::new(Field::new(name, dtype, true)));
147    }
148
149    Ok((Schema::new(input_fields), Schema::new(output_fields)))
150}
151
152fn resolve_function_signature(signature: &str) -> Result<(alloy_json_abi::Function, DynSolCall)> {
153    let event = alloy_json_abi::Function::parse(signature).context("parse function signature")?;
154    let resolved = event.resolve().context("resolve function signature")?;
155
156    Ok((event, resolved))
157}
158
159/// Decodes given event data in arrow format to arrow format.
160/// Output Arrow schema is auto generated based on the event signature.
161/// Handles any level of nesting with Lists/Structs.
162///
163/// Writes `null` for event data rows that fail to decode if `allow_decode_fail` is set to `true`.
164/// Errors when a row fails to decode if `allow_decode_fail` is set to `false`.
165pub fn decode_events(
166    signature: &str,
167    data: &RecordBatch,
168    allow_decode_fail: bool,
169) -> Result<RecordBatch> {
170    let (event, resolved) = resolve_event_signature(signature)?;
171
172    let schema = event_signature_to_arrow_schema_impl(&event, &resolved)
173        .context("convert event signature to arrow schema")?;
174
175    let mut arrays: Vec<Arc<dyn Array + 'static>> = Vec::with_capacity(schema.fields().len());
176
177    for (sol_type, topic_name) in resolved
178        .indexed()
179        .iter()
180        .zip(["topic1", "topic2", "topic3"].iter())
181    {
182        let col = data
183            .column_by_name(topic_name)
184            .context("get topic column")?
185            .as_any()
186            .downcast_ref::<BinaryArray>()
187            .context("get topic column as binary")?;
188
189        let mut decoded = Vec::<Option<DynSolValue>>::with_capacity(col.len());
190
191        for blob in col.iter() {
192            match blob {
193                Some(blob) => match sol_type.abi_decode(blob) {
194                    Ok(data) => decoded.push(Some(data)),
195                    Err(e) if allow_decode_fail => {
196                        log::debug!("failed to decode a topic: {}", e);
197                        decoded.push(None);
198                    }
199                    Err(e) => {
200                        return Err(anyhow!("failed to decode a topic: {}", e));
201                    }
202                },
203                None => decoded.push(None),
204            }
205        }
206
207        arrays.push(to_arrow(sol_type, decoded).context("map topic to arrow")?);
208    }
209
210    let body_col = data
211        .column_by_name("data")
212        .context("get data column")?
213        .as_any()
214        .downcast_ref::<BinaryArray>()
215        .context("get data column as binary")?;
216    let body_sol_type = DynSolType::Tuple(resolved.body().to_vec());
217
218    let mut body_decoded = Vec::<Option<DynSolValue>>::with_capacity(body_col.len());
219
220    for blob in body_col.iter() {
221        match blob {
222            Some(blob) => match body_sol_type.abi_decode_sequence(blob) {
223                Ok(data) => body_decoded.push(Some(data)),
224                Err(e) if allow_decode_fail => {
225                    log::debug!("failed to decode body: {}", e);
226                    body_decoded.push(None);
227                }
228                Err(e) => {
229                    return Err(anyhow!("failed to decode body: {}", e));
230                }
231            },
232            None => body_decoded.push(None),
233        }
234    }
235
236    let body_array = to_arrow(&body_sol_type, body_decoded).context("map body to arrow")?;
237    match body_array.data_type() {
238        DataType::Struct(_) => {
239            let arr = body_array.as_any().downcast_ref::<StructArray>().unwrap();
240
241            for f in arr.columns().iter() {
242                arrays.push(f.clone());
243            }
244        }
245        _ => unreachable!(),
246    }
247
248    RecordBatch::try_new(Arc::new(schema), arrays).context("construct arrow batch")
249}
250
251/// Generates Arrow schema based on given event signature
252pub fn event_signature_to_arrow_schema(signature: &str) -> Result<Schema> {
253    let (resolved, event) = resolve_event_signature(signature)?;
254    event_signature_to_arrow_schema_impl(&resolved, &event)
255}
256
257fn event_signature_to_arrow_schema_impl(
258    sig: &alloy_json_abi::Event,
259    event: &DynSolEvent,
260) -> Result<Schema> {
261    let num_fields = event.indexed().len() + event.body().len();
262    let mut fields = Vec::<Arc<Field>>::with_capacity(num_fields);
263    let mut names = Vec::with_capacity(num_fields);
264
265    for (i, input) in sig.inputs.iter().enumerate() {
266        if input.indexed {
267            let name = if input.name.is_empty() {
268                format!("param{}", i)
269            } else {
270                input.name.clone()
271            };
272            names.push(name);
273        }
274    }
275    for (i, input) in sig.inputs.iter().enumerate() {
276        if !input.indexed {
277            let name = if input.name.is_empty() {
278                format!("param{}", i)
279            } else {
280                input.name.clone()
281            };
282            names.push(name);
283        }
284    }
285
286    for (sol_t, name) in event.indexed().iter().chain(event.body()).zip(names) {
287        let dtype = to_arrow_dtype(sol_t).context("map to arrow type")?;
288        fields.push(Arc::new(Field::new(name, dtype, true)));
289    }
290
291    Ok(Schema::new(fields))
292}
293
294fn resolve_event_signature(signature: &str) -> Result<(alloy_json_abi::Event, DynSolEvent)> {
295    let event = alloy_json_abi::Event::parse(signature).context("parse event signature")?;
296    let resolved = event.resolve().context("resolve event signature")?;
297
298    Ok((event, resolved))
299}
300
301fn to_arrow_dtype(sol_type: &DynSolType) -> Result<DataType> {
302    match sol_type {
303        DynSolType::Bool => Ok(DataType::Boolean),
304        DynSolType::Bytes => Ok(DataType::Binary),
305        DynSolType::String => Ok(DataType::Utf8),
306        DynSolType::Address => Ok(DataType::Binary),
307        DynSolType::Int(num_bits) => Ok(num_bits_to_int_type(*num_bits)),
308        DynSolType::Uint(num_bits) => Ok(num_bits_to_uint_type(*num_bits)),
309        DynSolType::Array(inner_type) => {
310            let inner_type = to_arrow_dtype(inner_type).context("map inner")?;
311            Ok(DataType::List(Arc::new(Field::new("", inner_type, true))))
312        }
313        DynSolType::Function => Err(anyhow!(
314            "decoding 'Function' typed value in function signature isn't supported."
315        )),
316        DynSolType::FixedArray(inner_type, _) => {
317            let inner_type = to_arrow_dtype(inner_type).context("map inner")?;
318            Ok(DataType::List(Arc::new(Field::new("", inner_type, true))))
319        }
320        DynSolType::Tuple(fields) => {
321            let mut arrow_fields = Vec::<Arc<Field>>::with_capacity(fields.len());
322
323            for (i, f) in fields.iter().enumerate() {
324                let inner_dt = to_arrow_dtype(f).context("map field dt")?;
325                arrow_fields.push(Arc::new(Field::new(format!("param{}", i), inner_dt, true)));
326            }
327
328            Ok(DataType::Struct(Fields::from(arrow_fields)))
329        }
330        DynSolType::FixedBytes(_) => Ok(DataType::Binary),
331    }
332}
333
334fn num_bits_to_uint_type(num_bits: usize) -> DataType {
335    if num_bits <= 8 {
336        DataType::UInt8
337    } else if num_bits <= 16 {
338        DataType::UInt16
339    } else if num_bits <= 32 {
340        DataType::UInt32
341    } else if num_bits <= 64 {
342        DataType::UInt64
343    } else if num_bits <= 128 {
344        DataType::Decimal128(38, 0)
345    } else if num_bits <= 256 {
346        DataType::Decimal256(76, 0)
347    } else {
348        unreachable!()
349    }
350}
351
352fn num_bits_to_int_type(num_bits: usize) -> DataType {
353    if num_bits <= 8 {
354        DataType::Int8
355    } else if num_bits <= 16 {
356        DataType::Int16
357    } else if num_bits <= 32 {
358        DataType::Int32
359    } else if num_bits <= 64 {
360        DataType::Int64
361    } else if num_bits <= 128 {
362        DataType::Decimal128(38, 0)
363    } else if num_bits <= 256 {
364        DataType::Decimal256(76, 0)
365    } else {
366        unreachable!()
367    }
368}
369
370fn to_arrow(sol_type: &DynSolType, sol_values: Vec<Option<DynSolValue>>) -> Result<Arc<dyn Array>> {
371    match sol_type {
372        DynSolType::Bool => to_bool(&sol_values),
373        DynSolType::Bytes => to_binary(&sol_values),
374        DynSolType::String => to_string(&sol_values),
375        DynSolType::Address => to_binary(&sol_values),
376        DynSolType::Int(num_bits) => to_int(*num_bits, &sol_values),
377        DynSolType::Uint(num_bits) => to_uint(*num_bits, &sol_values),
378        DynSolType::Array(inner_type) => to_list(inner_type, sol_values),
379        DynSolType::Function => Err(anyhow!(
380            "decoding 'Function' typed value in function signature isn't supported."
381        )),
382        DynSolType::FixedArray(inner_type, _) => to_list(inner_type, sol_values),
383        DynSolType::Tuple(fields) => to_struct(fields, sol_values),
384        DynSolType::FixedBytes(_) => to_binary(&sol_values),
385    }
386}
387
388fn to_int(num_bits: usize, sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
389    match num_bits_to_int_type(num_bits) {
390        DataType::Int8 => to_int_impl::<Int8Type>(num_bits, sol_values),
391        DataType::Int16 => to_int_impl::<Int16Type>(num_bits, sol_values),
392        DataType::Int32 => to_int_impl::<Int32Type>(num_bits, sol_values),
393        DataType::Int64 => to_int_impl::<Int64Type>(num_bits, sol_values),
394        DataType::Decimal128(_, _) => to_decimal128(num_bits, sol_values),
395        DataType::Decimal256(_, _) => to_decimal256(num_bits, sol_values),
396        _ => unreachable!(),
397    }
398}
399
400fn to_uint(num_bits: usize, sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
401    match num_bits_to_int_type(num_bits) {
402        DataType::UInt8 => to_int_impl::<UInt8Type>(num_bits, sol_values),
403        DataType::UInt16 => to_int_impl::<UInt16Type>(num_bits, sol_values),
404        DataType::UInt32 => to_int_impl::<UInt32Type>(num_bits, sol_values),
405        DataType::UInt64 => to_int_impl::<UInt64Type>(num_bits, sol_values),
406        DataType::Decimal128(_, _) => to_decimal128(num_bits, sol_values),
407        DataType::Decimal256(_, _) => to_decimal256(num_bits, sol_values),
408        _ => unreachable!(),
409    }
410}
411
412fn to_decimal128(num_bits: usize, sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
413    let mut builder = builder::Decimal128Builder::new();
414
415    for val in sol_values.iter() {
416        match val {
417            Some(val) => match val {
418                DynSolValue::Int(v, nb) => {
419                    assert_eq!(num_bits, *nb);
420
421                    let v = i128::try_from(*v).context("convert to i128")?;
422
423                    builder.append_value(v);
424                }
425                DynSolValue::Uint(v, nb) => {
426                    assert_eq!(num_bits, *nb);
427
428                    let v = i128::try_from(*v).context("convert to i128")?;
429
430                    builder.append_value(v);
431                }
432                _ => {
433                    return Err(anyhow!(
434                        "found unexpected value. Expected: bool, Found: {:?}",
435                        val
436                    ));
437                }
438            },
439            None => {
440                builder.append_null();
441            }
442        }
443    }
444
445    builder = builder.with_data_type(DataType::Decimal128(38, 0));
446
447    Ok(Arc::new(builder.finish()))
448}
449
450fn to_decimal256(num_bits: usize, sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
451    let mut builder = builder::Decimal256Builder::new();
452
453    for val in sol_values.iter() {
454        match val {
455            Some(val) => match val {
456                DynSolValue::Int(v, nb) => {
457                    assert_eq!(num_bits, *nb);
458
459                    let v = arrow::datatypes::i256::from_be_bytes(v.to_be_bytes::<32>());
460
461                    builder.append_value(v);
462                }
463                DynSolValue::Uint(v, nb) => {
464                    assert_eq!(num_bits, *nb);
465                    let v = I256::try_from(*v).context("map u256 to i256")?;
466
467                    builder
468                        .append_value(arrow::datatypes::i256::from_be_bytes(v.to_be_bytes::<32>()));
469                }
470                _ => {
471                    return Err(anyhow!(
472                        "found unexpected value. Expected: bool, Found: {:?}",
473                        val
474                    ));
475                }
476            },
477            None => {
478                builder.append_null();
479            }
480        }
481    }
482
483    builder = builder.with_data_type(DataType::Decimal256(76, 0));
484
485    Ok(Arc::new(builder.finish()))
486}
487
488fn to_int_impl<T>(num_bits: usize, sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>>
489where
490    T: ArrowPrimitiveType,
491    T::Native: TryFrom<I256> + TryFrom<U256>,
492{
493    let mut builder = builder::PrimitiveBuilder::<T>::new();
494
495    for val in sol_values.iter() {
496        match val {
497            Some(val) => match val {
498                DynSolValue::Int(v, nb) => {
499                    assert_eq!(num_bits, *nb);
500                    builder.append_value(match T::Native::try_from(*v) {
501                        Ok(v) => v,
502                        Err(_) => unreachable!(),
503                    });
504                }
505                DynSolValue::Uint(v, nb) => {
506                    assert_eq!(num_bits, *nb);
507                    builder.append_value(match T::Native::try_from(*v) {
508                        Ok(v) => v,
509                        Err(_) => unreachable!(),
510                    });
511                }
512                _ => {
513                    return Err(anyhow!(
514                        "found unexpected value. Expected: bool, Found: {:?}",
515                        val
516                    ));
517                }
518            },
519            None => {
520                builder.append_null();
521            }
522        }
523    }
524
525    Ok(Arc::new(builder.finish()))
526}
527
528fn to_list(sol_type: &DynSolType, sol_values: Vec<Option<DynSolValue>>) -> Result<Arc<dyn Array>> {
529    let mut lengths = Vec::with_capacity(sol_values.len());
530    let mut values = Vec::with_capacity(sol_values.len() * 2);
531    let mut validity = Vec::with_capacity(sol_values.len() * 2);
532
533    let mut all_valid = true;
534
535    for val in sol_values {
536        match val {
537            Some(val) => match val {
538                DynSolValue::Array(inner_vals) | DynSolValue::FixedArray(inner_vals) => {
539                    lengths.push(inner_vals.len());
540                    values.extend(inner_vals.into_iter().map(Some));
541                    validity.push(true);
542                }
543                _ => {
544                    return Err(anyhow!(
545                        "found unexpected value. Expected list type, Found: {:?}",
546                        val
547                    ));
548                }
549            },
550            None => {
551                lengths.push(0);
552                validity.push(false);
553                all_valid = false;
554            }
555        }
556    }
557
558    let values = to_arrow(sol_type, values).context("map inner")?;
559    let field = Field::new(
560        "",
561        to_arrow_dtype(sol_type).context("construct data type")?,
562        true,
563    );
564    let list_arr = ListArray::try_new(
565        Arc::new(field),
566        OffsetBuffer::from_lengths(lengths),
567        values,
568        if all_valid {
569            None
570        } else {
571            Some(NullBuffer::from(validity))
572        },
573    )
574    .context("construct list array")?;
575    Ok(Arc::new(list_arr))
576}
577
578fn to_struct(
579    fields: &[DynSolType],
580    sol_values: Vec<Option<DynSolValue>>,
581) -> Result<Arc<dyn Array>> {
582    let mut values = vec![Vec::with_capacity(sol_values.len()); fields.len()];
583
584    // unpack top layer of sol_values into columnar format
585    // since we recurse by calling to_arrow later in the function, this will eventually map to
586    // primitive types.
587    for val in sol_values.iter() {
588        match val {
589            Some(val) => match val {
590                DynSolValue::Tuple(inner_vals) => {
591                    if values.len() != inner_vals.len() {
592                        return Err(anyhow!(
593                            "found unexpected length tuple value. Expected: {}, Found: {}",
594                            values.len(),
595                            inner_vals.len()
596                        ));
597                    }
598                    for (v, inner) in values.iter_mut().zip(inner_vals) {
599                        v.push(Some(inner.clone()));
600                    }
601                }
602                _ => {
603                    return Err(anyhow!(
604                        "found unexpected value. Expected: tuple, Found: {:?}",
605                        val
606                    ));
607                }
608            },
609            None => {
610                for v in values.iter_mut() {
611                    v.push(None);
612                }
613            }
614        }
615    }
616
617    let mut arrays = Vec::with_capacity(fields.len());
618
619    for (sol_type, arr_vals) in fields.iter().zip(values.into_iter()) {
620        arrays.push(to_arrow(sol_type, arr_vals)?);
621    }
622
623    let fields = arrays
624        .iter()
625        .enumerate()
626        .map(|(i, arr)| Field::new(format!("param{}", i), arr.data_type().clone(), true))
627        .collect::<Vec<_>>();
628    let schema = Arc::new(Schema::new(fields));
629
630    let batch = RecordBatch::try_new(schema, arrays).context("construct record batch")?;
631
632    Ok(Arc::new(StructArray::from(batch)))
633}
634
635fn to_bool(sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
636    let mut builder = builder::BooleanBuilder::new();
637
638    for val in sol_values.iter() {
639        match val {
640            Some(val) => match val {
641                DynSolValue::Bool(b) => {
642                    builder.append_value(*b);
643                }
644                _ => {
645                    return Err(anyhow!(
646                        "found unexpected value. Expected: bool, Found: {:?}",
647                        val
648                    ));
649                }
650            },
651            None => {
652                builder.append_null();
653            }
654        }
655    }
656
657    Ok(Arc::new(builder.finish()))
658}
659
660fn to_binary(sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
661    let mut builder = builder::BinaryBuilder::new();
662
663    for val in sol_values.iter() {
664        match val {
665            Some(val) => match val {
666                DynSolValue::Bytes(data) => {
667                    builder.append_value(data);
668                }
669                DynSolValue::FixedBytes(data, _) => {
670                    builder.append_value(data);
671                }
672                DynSolValue::Address(data) => {
673                    builder.append_value(data);
674                }
675                DynSolValue::Uint(v, _) => {
676                    builder.append_value(v.to_be_bytes::<32>());
677                }
678                DynSolValue::Int(v, _) => {
679                    builder.append_value(v.to_be_bytes::<32>());
680                }
681                _ => {
682                    return Err(anyhow!(
683                        "found unexpected value. Expected a binary type, Found: {:?}",
684                        val
685                    ));
686                }
687            },
688            None => {
689                builder.append_null();
690            }
691        }
692    }
693
694    Ok(Arc::new(builder.finish()))
695}
696
697fn to_string(sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
698    let mut builder = builder::StringBuilder::new();
699
700    for val in sol_values.iter() {
701        match val {
702            Some(val) => match val {
703                DynSolValue::String(s) => {
704                    builder.append_value(s);
705                }
706                _ => {
707                    return Err(anyhow!(
708                        "found unexpected value. Expected string, Found: {:?}",
709                        val
710                    ));
711                }
712            },
713            None => {
714                builder.append_null();
715            }
716        }
717    }
718
719    Ok(Arc::new(builder.finish()))
720}
721
722#[cfg(test)]
723mod tests {
724    use super::*;
725
726    #[test]
727    fn nested_event_signature_to_schema() {
728        let sig = "ConfiguredQuests(address editor, uint256[][], address indexed my_addr, (bool,bool[],(bool, uint256[]))[] questDetails)";
729
730        let schema = event_signature_to_arrow_schema(sig).unwrap();
731
732        let expected_schema = Schema::new(vec![
733            Arc::new(Field::new("my_addr", DataType::Binary, true)),
734            Arc::new(Field::new("editor", DataType::Binary, true)),
735            Arc::new(Field::new(
736                "param1",
737                DataType::List(Arc::new(Field::new(
738                    "",
739                    DataType::List(Arc::new(Field::new("", DataType::Decimal256(76, 0), true))),
740                    true,
741                ))),
742                true,
743            )),
744            Arc::new(Field::new(
745                "questDetails",
746                DataType::List(Arc::new(Field::new(
747                    "",
748                    DataType::Struct(Fields::from(vec![
749                        Arc::new(Field::new("param0", DataType::Boolean, true)),
750                        Arc::new(Field::new(
751                            "param1",
752                            DataType::List(Arc::new(Field::new("", DataType::Boolean, true))),
753                            true,
754                        )),
755                        Arc::new(Field::new(
756                            "param2",
757                            DataType::Struct(Fields::from(vec![
758                                Arc::new(Field::new("param0", DataType::Boolean, true)),
759                                Arc::new(Field::new(
760                                    "param1",
761                                    DataType::List(Arc::new(Field::new(
762                                        "",
763                                        DataType::Decimal256(76, 0),
764                                        true,
765                                    ))),
766                                    true,
767                                )),
768                            ])),
769                            true,
770                        )),
771                    ])),
772                    true,
773                ))),
774                true,
775            )),
776        ]);
777
778        assert_eq!(schema, expected_schema);
779    }
780
781    #[test]
782    fn i256_to_arrow_i256() {
783        for val in [
784            I256::MIN,
785            I256::MAX,
786            I256::MAX / I256::try_from(2i32).unwrap(),
787        ] {
788            let out = arrow::datatypes::i256::from_be_bytes(val.to_be_bytes::<32>());
789
790            assert_eq!(val.to_string(), out.to_string());
791        }
792    }
793}