transaction-decoder 0.1.13

A CLI tool for decoding EVM transactions
Documentation
//! # EVM Transaction Decoder CLI
//!
//! This CLI tool decodes EVM transactions and shows calldata details,
//! resolves ENS names, and optionally decodes emitted events.
#![allow(dead_code)]

mod cli;
mod decoder;
mod ens;
mod printer;
mod tui;
mod types;
mod utils;

use alloy::consensus::Transaction as TransactionTrait;
use alloy::providers::{Provider, ProviderBuilder};
use alloy::rpc::types::BlockTransactionsKind;
use chrono::{prelude::DateTime, Utc};
use clap::Parser;
use crossterm::{
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use eyre::Result;
use ratatui::{backend::CrosstermBackend, Terminal};
use std::io;
use std::time::{Duration, UNIX_EPOCH};

use crate::cli::Args;
use crate::printer::print_transaction;
use crate::utils::check_for_updates;

/// Entry point of the CLI tool.
/// - Checks for crate updates.
/// - Parses CLI arguments.
/// - Queries the RPC provider for the transaction and receipt.
/// - Delegates to the appropriate decoding logic.
#[tokio::main]
async fn main() -> Result<()> {
    check_for_updates().await;
    let args = Args::parse();

    let provider = ProviderBuilder::new().on_http(args.rpc_url.parse()?);

    if let Some(receipt) = provider
        .get_transaction_receipt(args.transaction_hash.parse()?)
        .await?
    {
        match provider
            .get_transaction_by_hash(args.transaction_hash.parse()?)
            .await
        {
            Ok(Some(tx)) => {
                if args.plain {
                    print_transaction(tx, receipt, provider, args.logs, args.traces).await;
                } else {
                    // Fetch data needed for TUI
                    let chain_id = provider.get_chain_id().await.unwrap();

                    let block_time = provider
                        .get_block_by_hash(tx.block_hash.unwrap(), BlockTransactionsKind::Hashes)
                        .await
                        .unwrap()
                        .unwrap()
                        .header
                        .timestamp;

                    let d = UNIX_EPOCH + Duration::from_secs(block_time);
                    let datetime = DateTime::<Utc>::from(d);
                    let timestamp_str = format!(
                        "{}({})",
                        block_time,
                        datetime.format("%Y-%m-%dT%H:%M:%SZ").to_string()
                    );

                    let details = printer::format_transaction_data(
                        &tx,
                        &receipt,
                        &provider,
                        chain_id,
                        &timestamp_str,
                    )
                    .await;
                    let logs_data = printer::format_logs_data(receipt.inner.logs()).await;
                    let (overview_details, input_details) =
                        tui::app::split_transaction_details(details);

                    // Setup TUI
                    enable_raw_mode()?;
                    let mut stdout = io::stdout();
                    execute!(stdout, EnterAlternateScreen)?;
                    let backend = CrosstermBackend::new(stdout);
                    let mut terminal = Terminal::new(backend)?;

                    let gas_used = receipt.gas_used;
                    let gas_limit = tx.gas_limit();

                    let app = tui::app::App::new(
                        overview_details,
                        input_details,
                        logs_data,
                        gas_used,
                        gas_limit as u128,
                    );
                    let res = tui::event::run_app(&mut terminal, app);

                    // Restore terminal
                    disable_raw_mode()?;
                    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
                    terminal.show_cursor()?;

                    if let Err(err) = res {
                        println!("{:?}", err);
                    }
                }
            }
            Ok(None) => eprintln!("Transaction not found."),
            Err(e) => eprintln!("Failed to fetch transaction: {}", e),
        }
    } else {
        eprintln!("Failed to fetch transaction status");
    }

    Ok(())
}