tiders_evm_schema/
lib.rs

1//! # tiders-evm-schema
2//!
3//! Pre-built Apache Arrow schemas and record batch builders for EVM blockchain data.
4//!
5//! Provides [`Schema`] definitions and corresponding builder structs for the four
6//! core EVM data tables:
7//!
8//! - **Blocks** ([`blocks_schema`] / [`BlocksBuilder`]) — Block headers including
9//!   hash, gas, timestamps, withdrawals, and L1/L2-specific fields.
10//! - **Transactions** ([`transactions_schema`] / [`TransactionsBuilder`]) — Full
11//!   transaction data with receipt fields, access lists, and L1/L2 fee fields.
12//! - **Logs** ([`logs_schema`] / [`LogsBuilder`]) — Event logs with topic0–topic3
13//!   and data columns.
14//! - **Traces** ([`traces_schema`] / [`TracesBuilder`]) — Execution traces including
15//!   call type, gas, value, and trace address.
16//!
17//! Numeric fields that may exceed 64 bits (gas, value, timestamps) use
18//! `Decimal256(76, 0)`. Addresses and hashes are stored as `Binary`.
19
20use std::sync::Arc;
21
22use arrow::array::builder;
23use arrow::datatypes::{DataType, Field, Fields, Schema};
24use arrow::record_batch::RecordBatch;
25
26/// Returns the Arrow schema for EVM block data.
27pub fn blocks_schema() -> Schema {
28    Schema::new(vec![
29        Field::new("number", DataType::UInt64, true),
30        Field::new("hash", DataType::Binary, true),
31        Field::new("parent_hash", DataType::Binary, true),
32        Field::new("nonce", DataType::Binary, true),
33        Field::new("sha3_uncles", DataType::Binary, true),
34        Field::new("logs_bloom", DataType::Binary, true),
35        Field::new("transactions_root", DataType::Binary, true),
36        Field::new("state_root", DataType::Binary, true),
37        Field::new("receipts_root", DataType::Binary, true),
38        Field::new("miner", DataType::Binary, true),
39        Field::new("difficulty", DataType::Decimal256(76, 0), true),
40        Field::new("total_difficulty", DataType::Decimal256(76, 0), true),
41        Field::new("extra_data", DataType::Binary, true),
42        Field::new("size", DataType::Decimal256(76, 0), true),
43        Field::new("gas_limit", DataType::Decimal256(76, 0), true),
44        Field::new("gas_used", DataType::Decimal256(76, 0), true),
45        Field::new("timestamp", DataType::Decimal256(76, 0), true),
46        Field::new(
47            "uncles",
48            DataType::List(Arc::new(Field::new("item", DataType::Binary, true))),
49            true,
50        ),
51        Field::new("base_fee_per_gas", DataType::Decimal256(76, 0), true),
52        Field::new("blob_gas_used", DataType::Decimal256(76, 0), true),
53        Field::new("excess_blob_gas", DataType::Decimal256(76, 0), true),
54        Field::new("parent_beacon_block_root", DataType::Binary, true),
55        Field::new("withdrawals_root", DataType::Binary, true),
56        Field::new(
57            "withdrawals",
58            DataType::List(Arc::new(Field::new("item", withdrawal_dt(), true))),
59            true,
60        ),
61        Field::new("l1_block_number", DataType::UInt64, true),
62        Field::new("send_count", DataType::Decimal256(76, 0), true),
63        Field::new("send_root", DataType::Binary, true),
64        Field::new("mix_hash", DataType::Binary, true),
65    ])
66}
67
68fn withdrawal_dt() -> DataType {
69    DataType::Struct(Fields::from(vec![
70        Arc::new(Field::new("index", DataType::UInt64, true)),
71        Arc::new(Field::new("validator_index", DataType::UInt64, true)),
72        Arc::new(Field::new("address", DataType::Binary, true)),
73        Arc::new(Field::new("amount", DataType::Decimal256(76, 0), true)),
74    ]))
75}
76
77/// Returns the Arrow schema for EVM transaction data (including receipt fields).
78pub fn transactions_schema() -> Schema {
79    Schema::new(vec![
80        Field::new("block_hash", DataType::Binary, true),
81        Field::new("block_number", DataType::UInt64, true),
82        Field::new("from", DataType::Binary, true),
83        Field::new("gas", DataType::Decimal256(76, 0), true),
84        Field::new("gas_price", DataType::Decimal256(76, 0), true),
85        Field::new("hash", DataType::Binary, true),
86        Field::new("input", DataType::Binary, true),
87        Field::new("nonce", DataType::Decimal256(76, 0), true),
88        Field::new("to", DataType::Binary, true),
89        Field::new("transaction_index", DataType::UInt64, true),
90        Field::new("value", DataType::Decimal256(76, 0), true),
91        Field::new("v", DataType::UInt8, true),
92        Field::new("r", DataType::Binary, true),
93        Field::new("s", DataType::Binary, true),
94        Field::new(
95            "max_priority_fee_per_gas",
96            DataType::Decimal256(76, 0),
97            true,
98        ),
99        Field::new("max_fee_per_gas", DataType::Decimal256(76, 0), true),
100        Field::new("chain_id", DataType::Decimal256(76, 0), true),
101        Field::new("cumulative_gas_used", DataType::Decimal256(76, 0), true),
102        Field::new("effective_gas_price", DataType::Decimal256(76, 0), true),
103        Field::new("gas_used", DataType::Decimal256(76, 0), true),
104        Field::new("contract_address", DataType::Binary, true),
105        Field::new("logs_bloom", DataType::Binary, true),
106        Field::new("type", DataType::UInt8, true),
107        Field::new("root", DataType::Binary, true),
108        Field::new("status", DataType::UInt8, true),
109        Field::new("sighash", DataType::Binary, true),
110        Field::new("y_parity", DataType::Boolean, true),
111        Field::new(
112            "access_list",
113            DataType::List(Arc::new(Field::new("item", access_list_elem_dt(), true))),
114            true,
115        ),
116        Field::new("l1_fee", DataType::Decimal256(76, 0), true),
117        Field::new("l1_gas_price", DataType::Decimal256(76, 0), true),
118        Field::new("l1_gas_used", DataType::Decimal256(76, 0), true),
119        Field::new("l1_fee_scalar", DataType::Decimal256(76, 0), true),
120        Field::new("gas_used_for_l1", DataType::Decimal256(76, 0), true),
121        Field::new("max_fee_per_blob_gas", DataType::Decimal256(76, 0), true),
122        Field::new(
123            "blob_versioned_hashes",
124            DataType::List(Arc::new(Field::new("item", DataType::Binary, true))),
125            true,
126        ),
127        Field::new("deposit_nonce", DataType::Decimal256(76, 0), true),
128        Field::new("blob_gas_price", DataType::Decimal256(76, 0), true),
129        Field::new("deposit_receipt_version", DataType::Decimal256(76, 0), true),
130        Field::new("blob_gas_used", DataType::Decimal256(76, 0), true),
131        Field::new("l1_base_fee_scalar", DataType::Decimal256(76, 0), true),
132        Field::new("l1_blob_base_fee", DataType::Decimal256(76, 0), true),
133        Field::new("l1_blob_base_fee_scalar", DataType::Decimal256(76, 0), true),
134        Field::new("l1_block_number", DataType::UInt64, true),
135        Field::new("mint", DataType::Decimal256(76, 0), true),
136        Field::new("source_hash", DataType::Binary, true),
137    ])
138}
139
140fn access_list_elem_dt() -> DataType {
141    DataType::Struct(Fields::from(vec![
142        Arc::new(Field::new("address", DataType::Binary, true)),
143        Arc::new(Field::new(
144            "storage_keys",
145            DataType::List(Arc::new(Field::new("item", DataType::Binary, true))),
146            true,
147        )),
148    ]))
149}
150
151/// Returns the Arrow schema for EVM event log data.
152pub fn logs_schema() -> Schema {
153    Schema::new(vec![
154        Field::new("removed", DataType::Boolean, true),
155        Field::new("log_index", DataType::UInt64, true),
156        Field::new("transaction_index", DataType::UInt64, true),
157        Field::new("transaction_hash", DataType::Binary, true),
158        Field::new("block_hash", DataType::Binary, true),
159        Field::new("block_number", DataType::UInt64, true),
160        Field::new("address", DataType::Binary, true),
161        Field::new("data", DataType::Binary, true),
162        Field::new("topic0", DataType::Binary, true),
163        Field::new("topic1", DataType::Binary, true),
164        Field::new("topic2", DataType::Binary, true),
165        Field::new("topic3", DataType::Binary, true),
166    ])
167}
168
169/// Returns the Arrow schema for EVM execution trace data.
170pub fn traces_schema() -> Schema {
171    Schema::new(vec![
172        Field::new("from", DataType::Binary, true),
173        Field::new("to", DataType::Binary, true),
174        Field::new("call_type", DataType::Utf8, true),
175        Field::new("gas", DataType::Decimal256(76, 0), true),
176        Field::new("input", DataType::Binary, true),
177        Field::new("init", DataType::Binary, true),
178        Field::new("value", DataType::Decimal256(76, 0), true),
179        Field::new("author", DataType::Binary, true),
180        Field::new("reward_type", DataType::Utf8, true),
181        Field::new("block_hash", DataType::Binary, true),
182        Field::new("block_number", DataType::UInt64, true),
183        Field::new("address", DataType::Binary, true),
184        Field::new("code", DataType::Binary, true),
185        Field::new("gas_used", DataType::Decimal256(76, 0), true),
186        Field::new("output", DataType::Binary, true),
187        Field::new("subtraces", DataType::UInt64, true),
188        Field::new(
189            "trace_address",
190            DataType::List(Arc::new(Field::new("item", DataType::UInt64, true))),
191            true,
192        ),
193        Field::new("transaction_hash", DataType::Binary, true),
194        Field::new("transaction_position", DataType::UInt64, true),
195        Field::new("type", DataType::Utf8, true),
196        Field::new("error", DataType::Utf8, true),
197        Field::new("sighash", DataType::Binary, true),
198        Field::new("action_address", DataType::Binary, true),
199        Field::new("balance", DataType::Decimal256(76, 0), true),
200        Field::new("refund_address", DataType::Binary, true),
201    ])
202}
203
204/// Builder for constructing an EVM blocks RecordBatch row-by-row.
205#[derive(Default)]
206pub struct BlocksBuilder {
207    pub number: builder::UInt64Builder,
208    pub hash: builder::BinaryBuilder,
209    pub parent_hash: builder::BinaryBuilder,
210    pub nonce: builder::BinaryBuilder,
211    pub sha3_uncles: builder::BinaryBuilder,
212    pub logs_bloom: builder::BinaryBuilder,
213    pub transactions_root: builder::BinaryBuilder,
214    pub state_root: builder::BinaryBuilder,
215    pub receipts_root: builder::BinaryBuilder,
216    pub miner: builder::BinaryBuilder,
217    pub difficulty: builder::Decimal256Builder,
218    pub total_difficulty: builder::Decimal256Builder,
219    pub extra_data: builder::BinaryBuilder,
220    pub size: builder::Decimal256Builder,
221    pub gas_limit: builder::Decimal256Builder,
222    pub gas_used: builder::Decimal256Builder,
223    pub timestamp: builder::Decimal256Builder,
224    pub uncles: builder::ListBuilder<builder::BinaryBuilder>,
225    pub base_fee_per_gas: builder::Decimal256Builder,
226    pub blob_gas_used: builder::Decimal256Builder,
227    pub excess_blob_gas: builder::Decimal256Builder,
228    pub parent_beacon_block_root: builder::BinaryBuilder,
229    pub withdrawals_root: builder::BinaryBuilder,
230    pub withdrawals: WithdrawalsBuilder,
231    pub l1_block_number: builder::UInt64Builder,
232    pub send_count: builder::Decimal256Builder,
233    pub send_root: builder::BinaryBuilder,
234    pub mix_hash: builder::BinaryBuilder,
235}
236
237/// Builder for the nested withdrawals list within a block.
238pub struct WithdrawalsBuilder(pub builder::ListBuilder<builder::StructBuilder>);
239
240impl Default for WithdrawalsBuilder {
241    #[expect(
242        clippy::unwrap_used,
243        reason = "precision 76, scale 0 is always valid for Decimal256"
244    )]
245    fn default() -> Self {
246        Self(builder::ListBuilder::new(builder::StructBuilder::new(
247            match withdrawal_dt() {
248                DataType::Struct(fields) => fields,
249                _ => unreachable!(),
250            },
251            vec![
252                Box::new(builder::UInt64Builder::default()),
253                Box::new(builder::UInt64Builder::default()),
254                Box::new(builder::BinaryBuilder::default()),
255                Box::new(
256                    builder::Decimal256Builder::default()
257                        .with_precision_and_scale(76, 0)
258                        .unwrap(),
259                ),
260            ],
261        )))
262    }
263}
264
265impl BlocksBuilder {
266    /// Consumes the builder and returns the completed RecordBatch.
267    #[expect(
268        clippy::unwrap_used,
269        reason = "schema and precision/scale are compile-time constants"
270    )]
271    pub fn finish(mut self) -> RecordBatch {
272        RecordBatch::try_new(
273            Arc::new(blocks_schema()),
274            vec![
275                Arc::new(self.number.finish()),
276                Arc::new(self.hash.finish()),
277                Arc::new(self.parent_hash.finish()),
278                Arc::new(self.nonce.finish()),
279                Arc::new(self.sha3_uncles.finish()),
280                Arc::new(self.logs_bloom.finish()),
281                Arc::new(self.transactions_root.finish()),
282                Arc::new(self.state_root.finish()),
283                Arc::new(self.receipts_root.finish()),
284                Arc::new(self.miner.finish()),
285                Arc::new(
286                    self.difficulty
287                        .with_precision_and_scale(76, 0)
288                        .unwrap()
289                        .finish(),
290                ),
291                Arc::new(
292                    self.total_difficulty
293                        .with_precision_and_scale(76, 0)
294                        .unwrap()
295                        .finish(),
296                ),
297                Arc::new(self.extra_data.finish()),
298                Arc::new(self.size.with_precision_and_scale(76, 0).unwrap().finish()),
299                Arc::new(
300                    self.gas_limit
301                        .with_precision_and_scale(76, 0)
302                        .unwrap()
303                        .finish(),
304                ),
305                Arc::new(
306                    self.gas_used
307                        .with_precision_and_scale(76, 0)
308                        .unwrap()
309                        .finish(),
310                ),
311                Arc::new(
312                    self.timestamp
313                        .with_precision_and_scale(76, 0)
314                        .unwrap()
315                        .finish(),
316                ),
317                Arc::new(self.uncles.finish()),
318                Arc::new(
319                    self.base_fee_per_gas
320                        .with_precision_and_scale(76, 0)
321                        .unwrap()
322                        .finish(),
323                ),
324                Arc::new(
325                    self.blob_gas_used
326                        .with_precision_and_scale(76, 0)
327                        .unwrap()
328                        .finish(),
329                ),
330                Arc::new(
331                    self.excess_blob_gas
332                        .with_precision_and_scale(76, 0)
333                        .unwrap()
334                        .finish(),
335                ),
336                Arc::new(self.parent_beacon_block_root.finish()),
337                Arc::new(self.withdrawals_root.finish()),
338                Arc::new(self.withdrawals.0.finish()),
339                Arc::new(self.l1_block_number.finish()),
340                Arc::new(
341                    self.send_count
342                        .with_precision_and_scale(76, 0)
343                        .unwrap()
344                        .finish(),
345                ),
346                Arc::new(self.send_root.finish()),
347                Arc::new(self.mix_hash.finish()),
348            ],
349        )
350        .unwrap()
351    }
352}
353
354/// Builder for constructing an EVM transactions RecordBatch row-by-row.
355#[derive(Default)]
356pub struct TransactionsBuilder {
357    pub block_hash: builder::BinaryBuilder,
358    pub block_number: builder::UInt64Builder,
359    pub from: builder::BinaryBuilder,
360    pub gas: builder::Decimal256Builder,
361    pub gas_price: builder::Decimal256Builder,
362    pub hash: builder::BinaryBuilder,
363    pub input: builder::BinaryBuilder,
364    pub nonce: builder::Decimal256Builder,
365    pub to: builder::BinaryBuilder,
366    pub transaction_index: builder::UInt64Builder,
367    pub value: builder::Decimal256Builder,
368    pub v: builder::UInt8Builder,
369    /// Kept as `Binary` even though r/s are uint256, because they don't fit in i256 in practice.
370    pub r: builder::BinaryBuilder,
371    /// Kept as `Binary` even though r/s are uint256, because they don't fit in i256 in practice.
372    pub s: builder::BinaryBuilder,
373    pub max_priority_fee_per_gas: builder::Decimal256Builder,
374    pub max_fee_per_gas: builder::Decimal256Builder,
375    pub chain_id: builder::Decimal256Builder,
376    pub cumulative_gas_used: builder::Decimal256Builder,
377    pub effective_gas_price: builder::Decimal256Builder,
378    pub gas_used: builder::Decimal256Builder,
379    pub contract_address: builder::BinaryBuilder,
380    pub logs_bloom: builder::BinaryBuilder,
381    pub type_: builder::UInt8Builder,
382    pub root: builder::BinaryBuilder,
383    pub status: builder::UInt8Builder,
384    pub sighash: builder::BinaryBuilder,
385    pub y_parity: builder::BooleanBuilder,
386    pub access_list: AccessListBuilder,
387    pub l1_fee: builder::Decimal256Builder,
388    pub l1_gas_price: builder::Decimal256Builder,
389    pub l1_gas_used: builder::Decimal256Builder,
390    pub l1_fee_scalar: builder::Decimal256Builder,
391    pub gas_used_for_l1: builder::Decimal256Builder,
392    pub max_fee_per_blob_gas: builder::Decimal256Builder,
393    pub blob_versioned_hashes: builder::ListBuilder<builder::BinaryBuilder>,
394    pub deposit_nonce: builder::Decimal256Builder,
395    pub blob_gas_price: builder::Decimal256Builder,
396    pub deposit_receipt_version: builder::Decimal256Builder,
397    pub blob_gas_used: builder::Decimal256Builder,
398    pub l1_base_fee_scalar: builder::Decimal256Builder,
399    pub l1_blob_base_fee: builder::Decimal256Builder,
400    pub l1_blob_base_fee_scalar: builder::Decimal256Builder,
401    pub l1_block_number: builder::UInt64Builder,
402    pub mint: builder::Decimal256Builder,
403    pub source_hash: builder::BinaryBuilder,
404}
405
406/// Builder for the nested access list within a transaction.
407pub struct AccessListBuilder(pub builder::ListBuilder<builder::StructBuilder>);
408
409impl Default for AccessListBuilder {
410    fn default() -> Self {
411        Self(builder::ListBuilder::new(builder::StructBuilder::new(
412            match access_list_elem_dt() {
413                DataType::Struct(fields) => fields,
414                _ => unreachable!(),
415            },
416            vec![
417                Box::new(builder::BinaryBuilder::default()),
418                Box::new(builder::ListBuilder::new(builder::BinaryBuilder::default())),
419            ],
420        )))
421    }
422}
423
424impl TransactionsBuilder {
425    /// Consumes the builder and returns the completed RecordBatch.
426    #[expect(
427        clippy::unwrap_used,
428        reason = "schema and precision/scale are compile-time constants"
429    )]
430    pub fn finish(mut self) -> RecordBatch {
431        RecordBatch::try_new(
432            Arc::new(transactions_schema()),
433            vec![
434                Arc::new(self.block_hash.finish()),
435                Arc::new(self.block_number.finish()),
436                Arc::new(self.from.finish()),
437                Arc::new(self.gas.with_precision_and_scale(76, 0).unwrap().finish()),
438                Arc::new(
439                    self.gas_price
440                        .with_precision_and_scale(76, 0)
441                        .unwrap()
442                        .finish(),
443                ),
444                Arc::new(self.hash.finish()),
445                Arc::new(self.input.finish()),
446                Arc::new(self.nonce.with_precision_and_scale(76, 0).unwrap().finish()),
447                Arc::new(self.to.finish()),
448                Arc::new(self.transaction_index.finish()),
449                Arc::new(self.value.with_precision_and_scale(76, 0).unwrap().finish()),
450                Arc::new(self.v.finish()),
451                Arc::new(self.r.finish()),
452                Arc::new(self.s.finish()),
453                Arc::new(
454                    self.max_priority_fee_per_gas
455                        .with_precision_and_scale(76, 0)
456                        .unwrap()
457                        .finish(),
458                ),
459                Arc::new(
460                    self.max_fee_per_gas
461                        .with_precision_and_scale(76, 0)
462                        .unwrap()
463                        .finish(),
464                ),
465                Arc::new(
466                    self.chain_id
467                        .with_precision_and_scale(76, 0)
468                        .unwrap()
469                        .finish(),
470                ),
471                Arc::new(
472                    self.cumulative_gas_used
473                        .with_precision_and_scale(76, 0)
474                        .unwrap()
475                        .finish(),
476                ),
477                Arc::new(
478                    self.effective_gas_price
479                        .with_precision_and_scale(76, 0)
480                        .unwrap()
481                        .finish(),
482                ),
483                Arc::new(
484                    self.gas_used
485                        .with_precision_and_scale(76, 0)
486                        .unwrap()
487                        .finish(),
488                ),
489                Arc::new(self.contract_address.finish()),
490                Arc::new(self.logs_bloom.finish()),
491                Arc::new(self.type_.finish()),
492                Arc::new(self.root.finish()),
493                Arc::new(self.status.finish()),
494                Arc::new(self.sighash.finish()),
495                Arc::new(self.y_parity.finish()),
496                Arc::new(self.access_list.0.finish()),
497                Arc::new(
498                    self.l1_fee
499                        .with_precision_and_scale(76, 0)
500                        .unwrap()
501                        .finish(),
502                ),
503                Arc::new(
504                    self.l1_gas_price
505                        .with_precision_and_scale(76, 0)
506                        .unwrap()
507                        .finish(),
508                ),
509                Arc::new(
510                    self.l1_gas_used
511                        .with_precision_and_scale(76, 0)
512                        .unwrap()
513                        .finish(),
514                ),
515                Arc::new(
516                    self.l1_fee_scalar
517                        .with_precision_and_scale(76, 0)
518                        .unwrap()
519                        .finish(),
520                ),
521                Arc::new(
522                    self.gas_used_for_l1
523                        .with_precision_and_scale(76, 0)
524                        .unwrap()
525                        .finish(),
526                ),
527                Arc::new(
528                    self.max_fee_per_blob_gas
529                        .with_precision_and_scale(76, 0)
530                        .unwrap()
531                        .finish(),
532                ),
533                Arc::new(self.blob_versioned_hashes.finish()),
534                Arc::new(
535                    self.deposit_nonce
536                        .with_precision_and_scale(76, 0)
537                        .unwrap()
538                        .finish(),
539                ),
540                Arc::new(
541                    self.blob_gas_price
542                        .with_precision_and_scale(76, 0)
543                        .unwrap()
544                        .finish(),
545                ),
546                Arc::new(
547                    self.deposit_receipt_version
548                        .with_precision_and_scale(76, 0)
549                        .unwrap()
550                        .finish(),
551                ),
552                Arc::new(
553                    self.blob_gas_used
554                        .with_precision_and_scale(76, 0)
555                        .unwrap()
556                        .finish(),
557                ),
558                Arc::new(
559                    self.l1_base_fee_scalar
560                        .with_precision_and_scale(76, 0)
561                        .unwrap()
562                        .finish(),
563                ),
564                Arc::new(
565                    self.l1_blob_base_fee
566                        .with_precision_and_scale(76, 0)
567                        .unwrap()
568                        .finish(),
569                ),
570                Arc::new(
571                    self.l1_blob_base_fee_scalar
572                        .with_precision_and_scale(76, 0)
573                        .unwrap()
574                        .finish(),
575                ),
576                Arc::new(self.l1_block_number.finish()),
577                Arc::new(self.mint.with_precision_and_scale(76, 0).unwrap().finish()),
578                Arc::new(self.source_hash.finish()),
579            ],
580        )
581        .unwrap()
582    }
583}
584
585/// Builder for constructing an EVM logs RecordBatch row-by-row.
586#[derive(Default)]
587pub struct LogsBuilder {
588    pub removed: builder::BooleanBuilder,
589    pub log_index: builder::UInt64Builder,
590    pub transaction_index: builder::UInt64Builder,
591    pub transaction_hash: builder::BinaryBuilder,
592    pub block_hash: builder::BinaryBuilder,
593    pub block_number: builder::UInt64Builder,
594    pub address: builder::BinaryBuilder,
595    pub data: builder::BinaryBuilder,
596    pub topic0: builder::BinaryBuilder,
597    pub topic1: builder::BinaryBuilder,
598    pub topic2: builder::BinaryBuilder,
599    pub topic3: builder::BinaryBuilder,
600}
601
602impl LogsBuilder {
603    /// Consumes the builder and returns the completed RecordBatch.
604    #[expect(clippy::unwrap_used, reason = "schema is a compile-time constant")]
605    pub fn finish(mut self) -> RecordBatch {
606        RecordBatch::try_new(
607            Arc::new(logs_schema()),
608            vec![
609                Arc::new(self.removed.finish()),
610                Arc::new(self.log_index.finish()),
611                Arc::new(self.transaction_index.finish()),
612                Arc::new(self.transaction_hash.finish()),
613                Arc::new(self.block_hash.finish()),
614                Arc::new(self.block_number.finish()),
615                Arc::new(self.address.finish()),
616                Arc::new(self.data.finish()),
617                Arc::new(self.topic0.finish()),
618                Arc::new(self.topic1.finish()),
619                Arc::new(self.topic2.finish()),
620                Arc::new(self.topic3.finish()),
621            ],
622        )
623        .unwrap()
624    }
625}
626
627/// Builder for constructing an EVM traces RecordBatch row-by-row.
628#[derive(Default)]
629pub struct TracesBuilder {
630    pub from: builder::BinaryBuilder,
631    pub to: builder::BinaryBuilder,
632    pub call_type: builder::StringBuilder,
633    pub gas: builder::Decimal256Builder,
634    pub input: builder::BinaryBuilder,
635    pub init: builder::BinaryBuilder,
636    pub value: builder::Decimal256Builder,
637    pub author: builder::BinaryBuilder,
638    pub reward_type: builder::StringBuilder,
639    pub block_hash: builder::BinaryBuilder,
640    pub block_number: builder::UInt64Builder,
641    pub address: builder::BinaryBuilder,
642    pub code: builder::BinaryBuilder,
643    pub gas_used: builder::Decimal256Builder,
644    pub output: builder::BinaryBuilder,
645    pub subtraces: builder::UInt64Builder,
646    pub trace_address: builder::ListBuilder<builder::UInt64Builder>,
647    pub transaction_hash: builder::BinaryBuilder,
648    pub transaction_position: builder::UInt64Builder,
649    pub type_: builder::StringBuilder,
650    pub error: builder::StringBuilder,
651    pub sighash: builder::BinaryBuilder,
652    pub action_address: builder::BinaryBuilder,
653    pub balance: builder::Decimal256Builder,
654    pub refund_address: builder::BinaryBuilder,
655}
656
657impl TracesBuilder {
658    /// Consumes the builder and returns the completed RecordBatch.
659    #[expect(
660        clippy::unwrap_used,
661        reason = "schema and precision/scale are compile-time constants"
662    )]
663    pub fn finish(mut self) -> RecordBatch {
664        RecordBatch::try_new(
665            Arc::new(traces_schema()),
666            vec![
667                Arc::new(self.from.finish()),
668                Arc::new(self.to.finish()),
669                Arc::new(self.call_type.finish()),
670                Arc::new(self.gas.with_precision_and_scale(76, 0).unwrap().finish()),
671                Arc::new(self.input.finish()),
672                Arc::new(self.init.finish()),
673                Arc::new(self.value.with_precision_and_scale(76, 0).unwrap().finish()),
674                Arc::new(self.author.finish()),
675                Arc::new(self.reward_type.finish()),
676                Arc::new(self.block_hash.finish()),
677                Arc::new(self.block_number.finish()),
678                Arc::new(self.address.finish()),
679                Arc::new(self.code.finish()),
680                Arc::new(
681                    self.gas_used
682                        .with_precision_and_scale(76, 0)
683                        .unwrap()
684                        .finish(),
685                ),
686                Arc::new(self.output.finish()),
687                Arc::new(self.subtraces.finish()),
688                Arc::new(self.trace_address.finish()),
689                Arc::new(self.transaction_hash.finish()),
690                Arc::new(self.transaction_position.finish()),
691                Arc::new(self.type_.finish()),
692                Arc::new(self.error.finish()),
693                Arc::new(self.sighash.finish()),
694                Arc::new(self.action_address.finish()),
695                Arc::new(
696                    self.balance
697                        .with_precision_and_scale(76, 0)
698                        .unwrap()
699                        .finish(),
700                ),
701                Arc::new(self.refund_address.finish()),
702            ],
703        )
704        .unwrap()
705    }
706}
707
708#[cfg(test)]
709mod tests {
710    use super::*;
711
712    #[test]
713    fn smoke() {
714        BlocksBuilder::default().finish();
715        TransactionsBuilder::default().finish();
716        LogsBuilder::default().finish();
717        TracesBuilder::default().finish();
718    }
719}