eth-etl-core 0.1.0

Core types and utilities for Ethereum ETL
Documentation
use serde::{Deserialize, Serialize};

/// Ethereum block with PoS withdrawals and EIP-4844 blob gas support
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EthBlock {
    #[serde(rename = "type", default = "default_block_type")]
    pub item_type: String,
    pub number: u64,
    pub hash: String,
    pub parent_hash: String,
    pub nonce: Option<String>,
    pub sha3_uncles: String,
    pub logs_bloom: String,
    pub transactions_root: String,
    pub state_root: String,
    pub receipts_root: String,
    pub miner: String,
    pub difficulty: String,
    pub total_difficulty: Option<String>,
    pub size: u64,
    pub extra_data: String,
    pub gas_limit: u64,
    pub gas_used: u64,
    pub timestamp: u64,
    pub transaction_count: usize,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub base_fee_per_gas: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub withdrawals_root: Option<String>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub withdrawals: Vec<Withdrawal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub blob_gas_used: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub excess_blob_gas: Option<u64>,
}

fn default_block_type() -> String {
    "block".to_string()
}

/// PoS withdrawal
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Withdrawal {
    pub index: u64,
    pub validator_index: u64,
    pub address: String,
    pub amount: u64,
}

impl EthBlock {
    /// Get CSV headers for block export
    pub fn csv_headers() -> &'static [&'static str] {
        &[
            "number",
            "hash",
            "parent_hash",
            "nonce",
            "sha3_uncles",
            "logs_bloom",
            "transactions_root",
            "state_root",
            "receipts_root",
            "miner",
            "difficulty",
            "total_difficulty",
            "size",
            "extra_data",
            "gas_limit",
            "gas_used",
            "timestamp",
            "transaction_count",
            "base_fee_per_gas",
            "withdrawals_root",
            "withdrawals",
            "blob_gas_used",
            "excess_blob_gas",
        ]
    }

    /// Convert to CSV row
    pub fn to_csv_row(&self) -> Vec<String> {
        vec![
            self.number.to_string(),
            self.hash.clone(),
            self.parent_hash.clone(),
            self.nonce.clone().unwrap_or_default(),
            self.sha3_uncles.clone(),
            self.logs_bloom.clone(),
            self.transactions_root.clone(),
            self.state_root.clone(),
            self.receipts_root.clone(),
            self.miner.clone(),
            self.difficulty.clone(),
            self.total_difficulty.clone().unwrap_or_default(),
            self.size.to_string(),
            self.extra_data.clone(),
            self.gas_limit.to_string(),
            self.gas_used.to_string(),
            self.timestamp.to_string(),
            self.transaction_count.to_string(),
            self.base_fee_per_gas
                .map(|v| v.to_string())
                .unwrap_or_default(),
            self.withdrawals_root.clone().unwrap_or_default(),
            serde_json::to_string(&self.withdrawals).unwrap_or_default(),
            self.blob_gas_used
                .map(|v| v.to_string())
                .unwrap_or_default(),
            self.excess_blob_gas
                .map(|v| v.to_string())
                .unwrap_or_default(),
        ]
    }
}