semioscan 0.11.2

Production-grade Rust library for blockchain analytics: gas calculation, price extraction, and block window calculations for EVM chains
Documentation
// SPDX-FileCopyrightText: 2025 Semiotic AI, Inc.
//
// SPDX-License-Identifier: Apache-2.0

//! Core gas calculation logic

use alloy_eips::Typed2718;
use alloy_primitives::{Address, BlockNumber, U256};
use alloy_rpc_types::{Filter, TransactionTrait};
use alloy_sol_types::SolEvent;

use crate::events::definitions::Transfer;
use crate::gas::transaction;

/// Core gas calculation logic
pub struct GasCalculationCore;

impl GasCalculationCore {
    pub(crate) fn calculate_blob_gas_cost<T>(transaction: &T) -> U256
    where
        T: TransactionTrait + Typed2718,
    {
        transaction::blob_gas_cost(transaction)
    }

    pub(crate) fn gas_price_override<T>(transaction: &T) -> Option<U256>
    where
        T: TransactionTrait + Typed2718,
    {
        transaction::gas_price_override(transaction)
    }

    pub(crate) fn create_transfer_filter(
        current_block: BlockNumber,
        to_block: BlockNumber,
        token_address: Address,
        from_address: Address, // topic1
        to_address: Address,   // topic2
    ) -> Filter {
        let transfer_topic_hash = Transfer::SIGNATURE_HASH;
        Filter::new()
            .from_block(current_block)
            .to_block(to_block)
            .address(token_address)
            .event_signature(transfer_topic_hash) // This takes B256, not Vec<B256>
            .topic1(from_address)
            .topic2(to_address)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::{address, Address};

    // ========== create_transfer_filter tests ==========
    //
    // Testing filter construction logic. The calculate_blob_gas_cost and
    // calculate_effective_gas_price functions are thin wrappers around
    // alloy's TransactionTrait methods and are tested via examples that
    // use real blockchain data.

    #[test]
    fn create_transfer_filter_sets_correct_block_range() {
        let current_block = 1000u64;
        let to_block = 2000u64;
        let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
        let from = address!("1111111111111111111111111111111111111111");
        let to = address!("2222222222222222222222222222222222222222");

        let filter =
            GasCalculationCore::create_transfer_filter(current_block, to_block, token, from, to);

        // Verify filter block range is set correctly
        assert_eq!(filter.get_from_block(), Some(1000));
        assert_eq!(filter.get_to_block(), Some(2000));
    }

    #[test]
    fn create_transfer_filter_handles_single_block_range() {
        let block = 5000u64;
        let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
        let from = Address::ZERO;
        let to = Address::ZERO;

        let filter = GasCalculationCore::create_transfer_filter(block, block, token, from, to);

        // Should handle single-block ranges correctly
        assert_eq!(filter.get_from_block(), Some(5000));
        assert_eq!(filter.get_to_block(), Some(5000));
    }

    #[test]
    fn create_transfer_filter_sets_correct_addresses() {
        let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
        let from = address!("1111111111111111111111111111111111111111");
        let to = address!("2222222222222222222222222222222222222222");

        let filter = GasCalculationCore::create_transfer_filter(100, 200, token, from, to);

        // Filter should be configured for correct token address
        // (Internal filter structure verification would require exposing internals)
        let _ = filter; // Use filter to avoid unused warning
    }

    #[test]
    fn create_transfer_filter_includes_transfer_event_signature() {
        let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
        let from = Address::ZERO;
        let to = Address::ZERO;

        let filter = GasCalculationCore::create_transfer_filter(1000, 2000, token, from, to);

        // The filter should include Transfer event signature
        // This is verified by the filter's successful use in production code
        let _ = filter;
    }
}