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, allow_decode_fail).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, allow_decode_fail).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 =
237        to_arrow(&body_sol_type, body_decoded, allow_decode_fail).context("map body to arrow")?;
238    match body_array.data_type() {
239        DataType::Struct(_) => {
240            let arr = body_array.as_any().downcast_ref::<StructArray>().unwrap();
241
242            for f in arr.columns().iter() {
243                arrays.push(f.clone());
244            }
245        }
246        _ => unreachable!(),
247    }
248
249    RecordBatch::try_new(Arc::new(schema), arrays).context("construct arrow batch")
250}
251
252/// Generates Arrow schema based on given event signature
253pub fn event_signature_to_arrow_schema(signature: &str) -> Result<Schema> {
254    let (resolved, event) = resolve_event_signature(signature)?;
255    event_signature_to_arrow_schema_impl(&resolved, &event)
256}
257
258fn event_signature_to_arrow_schema_impl(
259    sig: &alloy_json_abi::Event,
260    event: &DynSolEvent,
261) -> Result<Schema> {
262    let num_fields = event.indexed().len() + event.body().len();
263    let mut fields = Vec::<Arc<Field>>::with_capacity(num_fields);
264    let mut names = Vec::with_capacity(num_fields);
265
266    for (i, input) in sig.inputs.iter().enumerate() {
267        if input.indexed {
268            let name = if input.name.is_empty() {
269                format!("param{}", i)
270            } else {
271                input.name.clone()
272            };
273            names.push(name);
274        }
275    }
276    for (i, input) in sig.inputs.iter().enumerate() {
277        if !input.indexed {
278            let name = if input.name.is_empty() {
279                format!("param{}", i)
280            } else {
281                input.name.clone()
282            };
283            names.push(name);
284        }
285    }
286
287    for (sol_t, name) in event.indexed().iter().chain(event.body()).zip(names) {
288        let dtype = to_arrow_dtype(sol_t).context("map to arrow type")?;
289        fields.push(Arc::new(Field::new(name, dtype, true)));
290    }
291
292    Ok(Schema::new(fields))
293}
294
295fn resolve_event_signature(signature: &str) -> Result<(alloy_json_abi::Event, DynSolEvent)> {
296    let event = alloy_json_abi::Event::parse(signature).context("parse event signature")?;
297    let resolved = event.resolve().context("resolve event signature")?;
298
299    Ok((event, resolved))
300}
301
302fn to_arrow_dtype(sol_type: &DynSolType) -> Result<DataType> {
303    match sol_type {
304        DynSolType::Bool => Ok(DataType::Boolean),
305        DynSolType::Bytes => Ok(DataType::Binary),
306        DynSolType::String => Ok(DataType::Utf8),
307        DynSolType::Address => Ok(DataType::Binary),
308        DynSolType::Int(num_bits) => Ok(num_bits_to_int_type(*num_bits)),
309        DynSolType::Uint(num_bits) => Ok(num_bits_to_uint_type(*num_bits)),
310        DynSolType::Array(inner_type) => {
311            let inner_type = to_arrow_dtype(inner_type).context("map inner")?;
312            Ok(DataType::List(Arc::new(Field::new("", inner_type, true))))
313        }
314        DynSolType::Function => Err(anyhow!(
315            "decoding 'Function' typed value in function signature isn't supported."
316        )),
317        DynSolType::FixedArray(inner_type, _) => {
318            let inner_type = to_arrow_dtype(inner_type).context("map inner")?;
319            Ok(DataType::List(Arc::new(Field::new("", inner_type, true))))
320        }
321        DynSolType::Tuple(fields) => {
322            let mut arrow_fields = Vec::<Arc<Field>>::with_capacity(fields.len());
323
324            for (i, f) in fields.iter().enumerate() {
325                let inner_dt = to_arrow_dtype(f).context("map field dt")?;
326                arrow_fields.push(Arc::new(Field::new(format!("param{}", i), inner_dt, true)));
327            }
328
329            Ok(DataType::Struct(Fields::from(arrow_fields)))
330        }
331        DynSolType::FixedBytes(_) => Ok(DataType::Binary),
332    }
333}
334
335fn num_bits_to_uint_type(num_bits: usize) -> DataType {
336    if num_bits <= 8 {
337        DataType::UInt8
338    } else if num_bits <= 16 {
339        DataType::UInt16
340    } else if num_bits <= 32 {
341        DataType::UInt32
342    } else if num_bits <= 64 {
343        DataType::UInt64
344    } else if num_bits <= 128 {
345        DataType::Decimal128(38, 0)
346    } else if num_bits <= 256 {
347        DataType::Decimal256(76, 0)
348    } else {
349        unreachable!()
350    }
351}
352
353fn num_bits_to_int_type(num_bits: usize) -> DataType {
354    if num_bits <= 8 {
355        DataType::Int8
356    } else if num_bits <= 16 {
357        DataType::Int16
358    } else if num_bits <= 32 {
359        DataType::Int32
360    } else if num_bits <= 64 {
361        DataType::Int64
362    } else if num_bits <= 128 {
363        DataType::Decimal128(38, 0)
364    } else if num_bits <= 256 {
365        DataType::Decimal256(76, 0)
366    } else {
367        unreachable!()
368    }
369}
370
371fn to_arrow(
372    sol_type: &DynSolType,
373    sol_values: Vec<Option<DynSolValue>>,
374    allow_decode_fail: bool,
375) -> Result<Arc<dyn Array>> {
376    match sol_type {
377        DynSolType::Bool => to_bool(&sol_values),
378        DynSolType::Bytes => to_binary(&sol_values),
379        DynSolType::String => to_string(&sol_values),
380        DynSolType::Address => to_binary(&sol_values),
381        DynSolType::Int(num_bits) => to_int(*num_bits, &sol_values, allow_decode_fail),
382        DynSolType::Uint(num_bits) => to_uint(*num_bits, &sol_values, allow_decode_fail),
383        DynSolType::Array(inner_type) => to_list(inner_type, sol_values, allow_decode_fail),
384        DynSolType::Function => Err(anyhow!(
385            "decoding 'Function' typed value in function signature isn't supported."
386        )),
387        DynSolType::FixedArray(inner_type, _) => to_list(inner_type, sol_values, allow_decode_fail),
388        DynSolType::Tuple(fields) => to_struct(fields, sol_values, allow_decode_fail),
389        DynSolType::FixedBytes(_) => to_binary(&sol_values),
390    }
391}
392
393fn to_int(
394    num_bits: usize,
395    sol_values: &[Option<DynSolValue>],
396    allow_decode_fail: bool,
397) -> Result<Arc<dyn Array>> {
398    match num_bits_to_int_type(num_bits) {
399        DataType::Int8 => to_int_impl::<Int8Type>(num_bits, sol_values),
400        DataType::Int16 => to_int_impl::<Int16Type>(num_bits, sol_values),
401        DataType::Int32 => to_int_impl::<Int32Type>(num_bits, sol_values),
402        DataType::Int64 => to_int_impl::<Int64Type>(num_bits, sol_values),
403        DataType::Decimal128(_, _) => to_decimal128(num_bits, sol_values),
404        DataType::Decimal256(_, _) => to_decimal256(num_bits, sol_values, allow_decode_fail),
405        _ => unreachable!(),
406    }
407}
408
409fn to_uint(
410    num_bits: usize,
411    sol_values: &[Option<DynSolValue>],
412    allow_decode_fail: bool,
413) -> Result<Arc<dyn Array>> {
414    match num_bits_to_int_type(num_bits) {
415        DataType::UInt8 => to_int_impl::<UInt8Type>(num_bits, sol_values),
416        DataType::UInt16 => to_int_impl::<UInt16Type>(num_bits, sol_values),
417        DataType::UInt32 => to_int_impl::<UInt32Type>(num_bits, sol_values),
418        DataType::UInt64 => to_int_impl::<UInt64Type>(num_bits, sol_values),
419        DataType::Decimal128(_, _) => to_decimal128(num_bits, sol_values),
420        DataType::Decimal256(_, _) => to_decimal256(num_bits, sol_values, allow_decode_fail),
421        _ => unreachable!(),
422    }
423}
424
425fn to_decimal128(num_bits: usize, sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
426    let mut builder = builder::Decimal128Builder::new();
427
428    for val in sol_values.iter() {
429        match val {
430            Some(val) => match val {
431                DynSolValue::Int(v, nb) => {
432                    assert_eq!(num_bits, *nb);
433
434                    let v = i128::try_from(*v).context("convert to i128")?;
435
436                    builder.append_value(v);
437                }
438                DynSolValue::Uint(v, nb) => {
439                    assert_eq!(num_bits, *nb);
440
441                    let v = i128::try_from(*v).context("convert to i128")?;
442
443                    builder.append_value(v);
444                }
445                _ => {
446                    return Err(anyhow!(
447                        "found unexpected value. Expected: bool, Found: {:?}",
448                        val
449                    ));
450                }
451            },
452            None => {
453                builder.append_null();
454            }
455        }
456    }
457
458    builder = builder.with_data_type(DataType::Decimal128(38, 0));
459
460    Ok(Arc::new(builder.finish()))
461}
462
463fn to_decimal256(
464    num_bits: usize,
465    sol_values: &[Option<DynSolValue>],
466    allow_decode_fail: bool,
467) -> Result<Arc<dyn Array>> {
468    let mut builder = builder::Decimal256Builder::new();
469
470    for val in sol_values.iter() {
471        match val {
472            Some(val) => match val {
473                DynSolValue::Int(v, nb) => {
474                    assert_eq!(num_bits, *nb);
475
476                    let v = arrow::datatypes::i256::from_be_bytes(v.to_be_bytes::<32>());
477
478                    builder.append_value(v);
479                }
480                DynSolValue::Uint(v, nb) => {
481                    assert_eq!(num_bits, *nb);
482                    match I256::try_from(*v).context("try u256 to i256") {
483                        Ok(v) => builder.append_value(arrow::datatypes::i256::from_be_bytes(
484                            v.to_be_bytes::<32>(),
485                        )),
486                        Err(e) => {
487                            if allow_decode_fail {
488                                log::debug!("failed to decode u256: {}", e);
489                                builder.append_null();
490                            } else {
491                                return Err(e);
492                            }
493                        }
494                    }
495                }
496                _ => {
497                    return Err(anyhow!(
498                        "found unexpected value. Expected: bool, Found: {:?}",
499                        val
500                    ));
501                }
502            },
503            None => {
504                builder.append_null();
505            }
506        }
507    }
508
509    builder = builder.with_data_type(DataType::Decimal256(76, 0));
510
511    Ok(Arc::new(builder.finish()))
512}
513
514fn to_int_impl<T>(num_bits: usize, sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>>
515where
516    T: ArrowPrimitiveType,
517    T::Native: TryFrom<I256> + TryFrom<U256>,
518{
519    let mut builder = builder::PrimitiveBuilder::<T>::new();
520
521    for val in sol_values.iter() {
522        match val {
523            Some(val) => match val {
524                DynSolValue::Int(v, nb) => {
525                    assert_eq!(num_bits, *nb);
526                    builder.append_value(match T::Native::try_from(*v) {
527                        Ok(v) => v,
528                        Err(_) => unreachable!(),
529                    });
530                }
531                DynSolValue::Uint(v, nb) => {
532                    assert_eq!(num_bits, *nb);
533                    builder.append_value(match T::Native::try_from(*v) {
534                        Ok(v) => v,
535                        Err(_) => unreachable!(),
536                    });
537                }
538                _ => {
539                    return Err(anyhow!(
540                        "found unexpected value. Expected: bool, Found: {:?}",
541                        val
542                    ));
543                }
544            },
545            None => {
546                builder.append_null();
547            }
548        }
549    }
550
551    Ok(Arc::new(builder.finish()))
552}
553
554fn to_list(
555    sol_type: &DynSolType,
556    sol_values: Vec<Option<DynSolValue>>,
557    allow_decode_fail: bool,
558) -> Result<Arc<dyn Array>> {
559    let mut lengths = Vec::with_capacity(sol_values.len());
560    let mut values = Vec::with_capacity(sol_values.len() * 2);
561    let mut validity = Vec::with_capacity(sol_values.len() * 2);
562
563    let mut all_valid = true;
564
565    for val in sol_values {
566        match val {
567            Some(val) => match val {
568                DynSolValue::Array(inner_vals) | DynSolValue::FixedArray(inner_vals) => {
569                    lengths.push(inner_vals.len());
570                    values.extend(inner_vals.into_iter().map(Some));
571                    validity.push(true);
572                }
573                _ => {
574                    return Err(anyhow!(
575                        "found unexpected value. Expected list type, Found: {:?}",
576                        val
577                    ));
578                }
579            },
580            None => {
581                lengths.push(0);
582                validity.push(false);
583                all_valid = false;
584            }
585        }
586    }
587
588    let values = to_arrow(sol_type, values, allow_decode_fail).context("map inner")?;
589    let field = Field::new(
590        "",
591        to_arrow_dtype(sol_type).context("construct data type")?,
592        true,
593    );
594    let list_arr = ListArray::try_new(
595        Arc::new(field),
596        OffsetBuffer::from_lengths(lengths),
597        values,
598        if all_valid {
599            None
600        } else {
601            Some(NullBuffer::from(validity))
602        },
603    )
604    .context("construct list array")?;
605    Ok(Arc::new(list_arr))
606}
607
608fn to_struct(
609    fields: &[DynSolType],
610    sol_values: Vec<Option<DynSolValue>>,
611    allow_decode_fail: bool,
612) -> Result<Arc<dyn Array>> {
613    let mut values = vec![Vec::with_capacity(sol_values.len()); fields.len()];
614
615    // unpack top layer of sol_values into columnar format
616    // since we recurse by calling to_arrow later in the function, this will eventually map to
617    // primitive types.
618    for val in sol_values.iter() {
619        match val {
620            Some(val) => match val {
621                DynSolValue::Tuple(inner_vals) => {
622                    if values.len() != inner_vals.len() {
623                        return Err(anyhow!(
624                            "found unexpected length tuple value. Expected: {}, Found: {}",
625                            values.len(),
626                            inner_vals.len()
627                        ));
628                    }
629                    for (v, inner) in values.iter_mut().zip(inner_vals) {
630                        v.push(Some(inner.clone()));
631                    }
632                }
633                _ => {
634                    return Err(anyhow!(
635                        "found unexpected value. Expected: tuple, Found: {:?}",
636                        val
637                    ));
638                }
639            },
640            None => {
641                for v in values.iter_mut() {
642                    v.push(None);
643                }
644            }
645        }
646    }
647
648    let mut arrays = Vec::with_capacity(fields.len());
649
650    for (sol_type, arr_vals) in fields.iter().zip(values.into_iter()) {
651        arrays.push(to_arrow(sol_type, arr_vals, allow_decode_fail)?);
652    }
653
654    let fields = arrays
655        .iter()
656        .enumerate()
657        .map(|(i, arr)| Field::new(format!("param{}", i), arr.data_type().clone(), true))
658        .collect::<Vec<_>>();
659    let schema = Arc::new(Schema::new(fields));
660
661    let batch = RecordBatch::try_new(schema, arrays).context("construct record batch")?;
662
663    Ok(Arc::new(StructArray::from(batch)))
664}
665
666fn to_bool(sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
667    let mut builder = builder::BooleanBuilder::new();
668
669    for val in sol_values.iter() {
670        match val {
671            Some(val) => match val {
672                DynSolValue::Bool(b) => {
673                    builder.append_value(*b);
674                }
675                _ => {
676                    return Err(anyhow!(
677                        "found unexpected value. Expected: bool, Found: {:?}",
678                        val
679                    ));
680                }
681            },
682            None => {
683                builder.append_null();
684            }
685        }
686    }
687
688    Ok(Arc::new(builder.finish()))
689}
690
691fn to_binary(sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
692    let mut builder = builder::BinaryBuilder::new();
693
694    for val in sol_values.iter() {
695        match val {
696            Some(val) => match val {
697                DynSolValue::Bytes(data) => {
698                    builder.append_value(data);
699                }
700                DynSolValue::FixedBytes(data, _) => {
701                    builder.append_value(data);
702                }
703                DynSolValue::Address(data) => {
704                    builder.append_value(data);
705                }
706                DynSolValue::Uint(v, _) => {
707                    builder.append_value(v.to_be_bytes::<32>());
708                }
709                DynSolValue::Int(v, _) => {
710                    builder.append_value(v.to_be_bytes::<32>());
711                }
712                _ => {
713                    return Err(anyhow!(
714                        "found unexpected value. Expected a binary type, Found: {:?}",
715                        val
716                    ));
717                }
718            },
719            None => {
720                builder.append_null();
721            }
722        }
723    }
724
725    Ok(Arc::new(builder.finish()))
726}
727
728fn to_string(sol_values: &[Option<DynSolValue>]) -> Result<Arc<dyn Array>> {
729    let mut builder = builder::StringBuilder::new();
730
731    for val in sol_values.iter() {
732        match val {
733            Some(val) => match val {
734                DynSolValue::String(s) => {
735                    builder.append_value(s);
736                }
737                _ => {
738                    return Err(anyhow!(
739                        "found unexpected value. Expected string, Found: {:?}",
740                        val
741                    ));
742                }
743            },
744            None => {
745                builder.append_null();
746            }
747        }
748    }
749
750    Ok(Arc::new(builder.finish()))
751}
752
753#[cfg(test)]
754mod tests {
755    use super::*;
756
757    #[test]
758    fn nested_event_signature_to_schema() {
759        let sig = "ConfiguredQuests(address editor, uint256[][], address indexed my_addr, (bool,bool[],(bool, uint256[]))[] questDetails)";
760
761        let schema = event_signature_to_arrow_schema(sig).unwrap();
762
763        let expected_schema = Schema::new(vec![
764            Arc::new(Field::new("my_addr", DataType::Binary, true)),
765            Arc::new(Field::new("editor", DataType::Binary, true)),
766            Arc::new(Field::new(
767                "param1",
768                DataType::List(Arc::new(Field::new(
769                    "",
770                    DataType::List(Arc::new(Field::new("", DataType::Decimal256(76, 0), true))),
771                    true,
772                ))),
773                true,
774            )),
775            Arc::new(Field::new(
776                "questDetails",
777                DataType::List(Arc::new(Field::new(
778                    "",
779                    DataType::Struct(Fields::from(vec![
780                        Arc::new(Field::new("param0", DataType::Boolean, true)),
781                        Arc::new(Field::new(
782                            "param1",
783                            DataType::List(Arc::new(Field::new("", DataType::Boolean, true))),
784                            true,
785                        )),
786                        Arc::new(Field::new(
787                            "param2",
788                            DataType::Struct(Fields::from(vec![
789                                Arc::new(Field::new("param0", DataType::Boolean, true)),
790                                Arc::new(Field::new(
791                                    "param1",
792                                    DataType::List(Arc::new(Field::new(
793                                        "",
794                                        DataType::Decimal256(76, 0),
795                                        true,
796                                    ))),
797                                    true,
798                                )),
799                            ])),
800                            true,
801                        )),
802                    ])),
803                    true,
804                ))),
805                true,
806            )),
807        ]);
808
809        assert_eq!(schema, expected_schema);
810    }
811
812    #[test]
813    fn i256_to_arrow_i256() {
814        for val in [
815            I256::MIN,
816            I256::MAX,
817            I256::MAX / I256::try_from(2i32).unwrap(),
818        ] {
819            let out = arrow::datatypes::i256::from_be_bytes(val.to_be_bytes::<32>());
820
821            assert_eq!(val.to_string(), out.to_string());
822        }
823    }
824}