нужно отрефакторить этот код в части основной идеи - получение сведений о транзакциях в сети ethereum. Нужно получать как сведения об исторических транзакциях (метод fetch) так и транзакциях содержащихся в локальной базе кошельков в выходящих новых блоках, нужно чтобы код использовал современные библиотеки и не содержал логики ведущей к бессмысленному перебору, если он не необходим, например если есть метод апи позволяющий получить все транзакции по адресу сразу, нужно его ипользовать, а не перебирать весь блокчейн import { Injectable, Logger } from '@nestjs/common';

import { ethers, Contract, JsonRpcProvider, getAddress, id, formatEther, formatUnits } from 'ethers';

import { Model } from 'mongoose';

import { InjectModel } from '@nestjs/mongoose';

import axios from 'axios';

import { ConfigService } from '@nestjs/config';

import { MonitoredAddress, AddressType } from '../schemas/monitored-address.schema';

import { EthereumTransaction } from './schemas/ethereum-transaction.schema';

import { ProcessedEthBlocks } from './schemas/processed-eth-blocks.schema';

import { EthereumBlocksService } from './blocks/ethereum-blocks.service';

import pLimit from 'p-limit';

import { NotificationsService } from '../../notifications/notifications.service';

import { AddressBinding } from '../schemas/address-binding.schema';


interface EthereumTransactionData {

  _id: string;

  blockId: number;

  code: number;

  error?: string;

  tx: any;

  timestamp: Date;

  chainId: string;

  related: string[];

  type: string[];

  evts?: number;

  gasUsed: number;

  fee: number;

  amount?: string;

}


@Injectable()

export class EthereumService {

  private readonly logger = new Logger(EthereumService.name);

  private readonly provider: JsonRpcProvider;

  private readonly limit: ReturnType<typeof pLimit>;

  private readonly REQUEST_TIMEOUT_MS = 10000;

  private readonly BATCH_SIZE = 4;

  private readonly BATCH_DELAY_MS = 200;


  constructor(

    @InjectModel(MonitoredAddress.name, 'main')

    private readonly monitoredAddressModel: Model<MonitoredAddress>,

    @InjectModel(EthereumTransaction.name, 'operations')

    private readonly ethereumTransactionModel: Model<EthereumTransaction>,

    @InjectModel(ProcessedEthBlocks.name, 'operations')

    private readonly processedEthBlocksModel: Model<ProcessedEthBlocks>,

    @InjectModel(AddressBinding.name, 'main')

    private readonly addressBindingModel: Model<AddressBinding>,

    private readonly ethereumBlocksService: EthereumBlocksService,

    private readonly configService: ConfigService,

    private readonly notificationsService: NotificationsService,

  ) {

    const ethRpcUrl = this.configService.get('ETHEREUM_RPC_URL');

    this.provider = new ethers.JsonRpcProvider(ethRpcUrl);

    this.limit = pLimit(5);

  }


  private async getMonitoredAddresses(): Promise<Set<string>> {

    const monitoredAddresses = await this.monitoredAddressModel

      .find({ type: AddressType.ETH })

      .select('address')

      .lean();

    return new Set(monitoredAddresses.map((doc) => doc.address.toLowerCase()));

  }


  private async findAxmByEthAddress(ethAddress: string): Promise<string | null> {

    const binding = await this.addressBindingModel.findOne({ ethAddress: ethAddress.toLowerCase() }).lean();

    return binding ? binding.axmAddress : null;

  }


  private createBaseTransaction(tx: any, blockNumber: number, timestamp: number): EthereumTransactionData {

    return {

      _id: tx.hash,

      blockId: blockNumber,

      code: tx.status === 1 ? 0 : 1,

      error: tx.status === 1 ? undefined : 'FAILED',

      tx,

      timestamp: new Date(timestamp * 1000),

      chainId: 'mainnet',

      related: [],

      type: [],

      evts: 0,

      gasUsed: 0,

      fee: 0,

      amount: undefined,

    };

  }


  private async getGasAndFee(tx: any, receipt: any): Promise<{ gasUsed: number; fee: number }> {

    const gasUsed = Number(receipt.gasUsed);

    const fee = Number(formatEther(BigInt(gasUsed) * BigInt(tx.gasPrice || 0)));

    return { gasUsed, fee };

  }


  private async saveTransactions(transactionsToSave: EthereumTransactionData[]): Promise<void> {

    if (transactionsToSave.length > 0) {

      const bulkOps = transactionsToSave.map((tx) => ({

        updateOne: {

          filter: { _id: tx._id },

          update: { $set: tx },

          upsert: true,

        },

      }));

      await this.ethereumTransactionModel.bulkWrite(bulkOps);

      this.logger.log(`Saved ${transactionsToSave.length} transactions`);

    }

  }


  private async sendNotification(axmAddress: string, notificationType: string, amount: string, timestamp: number, ticker: string) {

    const data = {

      amount: BigInt(amount),

      ticker,

    };

    await this.notificationsService.sendNotification(axmAddress, notificationType, data, timestamp);

  }


  async getLastBlock(): Promise<any> {

    try {

      return await this.provider.getBlock('latest');

    } catch (error) {

      this.logger.error(`Error fetching latest block: ${error.message}`);

      throw new Error('Failed to fetch latest block');

    }

  }


  async getEthBalance(address: string): Promise<number> {

    try {

      const balance = await this.provider.getBalance(address);

      return Number(formatEther(balance));

    } catch (error) {

      this.logger.error(`Error fetching ETH balance for ${address}: ${error.message}`);

      throw error;

    }

  }


  async getUsdtBalance(address: string): Promise<number> {

    try {

      const usdtContractAddress = this.configService.get('ERC20_USDT_CONTRACT_ADDRESS') || '0xdAC17F958D2ee523a2206206994597C13D831ec7';

      const usdtAbi = ['function balanceOf(address) view returns (uint256)'];

      const contract = new Contract(usdtContractAddress, usdtAbi, this.provider);

      const balance = await contract.balanceOf(address);

      return Number(formatUnits(balance, 6)); // USDT uses 6 decimals

    } catch (error) {

      this.logger.error(`Error fetching USDT balance for ${address}: ${error.message}`);

      throw error;

    }

  }


  async analyzeBlockTransactions(blockNumber: number, save: boolean = false): Promise<void> {

    this.logger.log(`Starting analysis of block ${blockNumber}`);

    try {

      const block = await this.provider.getBlock(blockNumber, true);

      this.logger.log(`Successfully fetched block ${blockNumber}`);


      if (!block || Object.keys(block).length === 0) {

        throw new Error(`Invalid block data for block ${blockNumber}: empty response`);

      }


      if (!block.transactions || block.transactions.length === 0) {

        this.logger.log(`Block ${blockNumber} has no transactions`);

        if (save) {

          await this.processedEthBlocksModel.updateOne(

            { _id: blockNumber },

            { $set: { timestamp: block.timestamp, chainId: 'mainnet' } },

            { upsert: true },

          );

        }

        return;

      }


      this.logger.log(`Block ${blockNumber} contains ${block.transactions.length} transactions`);


      const addressSet = await this.getMonitoredAddresses();


      // Timeout wrapper for promises

      const withTimeout = (promise: Promise<any>, timeoutMs: number, errorMessage: string) => {

        return Promise.race([

          promise,

          new Promise((_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs)),

        ]);

      };


      const transactionsToSave: EthereumTransactionData[] = [];

      let ethTransactionsToSave = 0;

      let usdtTransactionsToSave = 0;


      // Process transactions in batches

      for (let i = 0; i < block.transactions.length; i += this.BATCH_SIZE) {

        const batch = block.transactions.slice(i, i + this.BATCH_SIZE);

        this.logger.log(`Processing batch ${i / this.BATCH_SIZE + 1} (${batch.length} transactions)`);


        const txPromises = batch.map((txHash: string) =>

          this.limit(() =>

            withTimeout(

              this.provider.getTransaction(txHash),

              this.REQUEST_TIMEOUT_MS,

              `Timeout fetching transaction ${txHash}`,

            ),

          ),

        );

        const txs = await Promise.all(txPromises);


        const receiptPromises = txs.map((tx) =>

          tx

            ? this.limit(() =>

              withTimeout(

                this.provider.getTransactionReceipt(tx.hash),

                this.REQUEST_TIMEOUT_MS,

                `Timeout fetching receipt for transaction ${tx.hash}`,

              ),

            )

            : Promise.resolve(null),

        );

        const receipts = await Promise.all(receiptPromises);


        for (let j = 0; j < txs.length; j++) {

          const tx = txs[j];

          if (!tx) continue;


          let transactionData = this.createBaseTransaction(tx, blockNumber, block.timestamp);

          const receipt = receipts[j];

          if (!receipt) continue;


          transactionData.code = receipt.status === 1 ? 0 : 1;

          transactionData.error = receipt.status === 1 ? undefined : 'FAILED';


          if (!tx.to) continue;


          const from = tx.from.toLowerCase();

          const to = tx.to.toLowerCase();


          if (!tx.data || tx.data === '0x') {

            const value = BigInt(tx.value);

            if (value < 0) {

              this.logger.warn(`Negative value detected for tx ${tx.hash}: ${value}, skipping`);

              continue;

            }


            if (addressSet.has(from) || addressSet.has(to)) {

              transactionData.related = [from, to];

              const { gasUsed, fee } = await this.getGasAndFee(tx, receipt);

              transactionData.type = ['ETH_TRANSFER'];

              transactionData.evts = 0;

              transactionData.amount = value.toString(); // Use amount field

              transactionData.gasUsed = gasUsed;

              transactionData.fee = fee;


              const axmToAddress = await this.findAxmByEthAddress(to);

              if (axmToAddress && addressSet.has(to)) {

                await this.sendNotification(axmToAddress, 'tx_receive', value.toString(), block.timestamp * 1000, 'ETH');

              }

              const axmFromAddress = await this.findAxmByEthAddress(from);

              if (axmFromAddress && addressSet.has(from)) {

                await this.sendNotification(axmFromAddress, 'tx_send', value.toString(), block.timestamp * 1000, 'ETH');

              }


              transactionsToSave.push(transactionData);

              ethTransactionsToSave++;

            }

          } else if (tx.to.toLowerCase() === (this.configService.get('ERC20_USDT_CONTRACT_ADDRESS') || '0xdAC17F958D2ee523a2206206994597C13D831ec7').toLowerCase()) {

            const logs = receipt.logs;

            const transferTopic = id('Transfer(address,address,uint256)');


            for (const log of logs) {

              if (log.topics[0] === transferTopic) {

                const from = getAddress('0x' + log.topics[1].slice(-40));

                const to = getAddress('0x' + log.topics[2].slice(-40));

                const value = BigInt(log.data);


                if (!from.match(/^0x[a-fA-F0-9]{40}$/) || !to.match(/^0x[a-fA-F0-9]{40}$/)) {

                  this.logger.warn(`Invalid address format for tx ${tx.hash}: from=${from}, to=${to}`);

                  continue;

                }


                if (value < 0) {

                  this.logger.warn(`Negative value detected for tx ${tx.hash}: ${value}, skipping`);

                  continue;

                }


                if (addressSet.has(from) || addressSet.has(to)) {

                  transactionData.related = [from, to];

                  const { gasUsed, fee } = await this.getGasAndFee(tx, receipt);

                  transactionData.type = ['USDT_TRANSFER'];

                  transactionData.evts = 1;

                  transactionData.amount = value.toString(); // Use amount field

                  transactionData.tx = {

                    ...tx,

                    from,

                    to,

                  };

                  transactionData.gasUsed = gasUsed;

                  transactionData.fee = fee;


                  const axmToAddress = await this.findAxmByEthAddress(to);

                  if (axmToAddress && addressSet.has(to)) {

                    await this.sendNotification(axmToAddress, 'tx_receive', value.toString(), block.timestamp * 1000, 'USDT');

                  }

                  const axmFromAddress = await this.findAxmByEthAddress(from);

                  if (axmFromAddress && addressSet.has(from)) {

                    await this.sendNotification(axmFromAddress, 'tx_send', value.toString(), block.timestamp * 1000, 'USDT');

                  }


                  transactionsToSave.push(transactionData);

                  usdtTransactionsToSave++;

                }

                break;

              }

            }

          }

        }


        if (i + this.BATCH_SIZE < block.transactions.length) {

          this.logger.log(`Waiting ${this.BATCH_DELAY_MS}ms before next batch`);

          await new Promise((resolve) => setTimeout(resolve, this.BATCH_DELAY_MS));

        }

      }


      this.logger.log(

        `Processed ${block.transactions.length} transactions, ${ethTransactionsToSave} ETH and ${usdtTransactionsToSave} USDT relevant for monitored addresses`,

      );


      await this.saveTransactions(transactionsToSave);


      if (save) {

        await this.processedEthBlocksModel.updateOne(

          { _id: blockNumber },

          { $set: { timestamp: block.timestamp, chainId: 'mainnet' } },

          { upsert: true },

        );

      }


      this.logger.log(`Completed analysis of block ${blockNumber}, saved ${transactionsToSave.length} transactions`);

    } catch (error) {

      if (

        error.code === 'RATE_LIMIT_EXCEEDED' ||

        error.message.includes('rate limit') ||

        error.message.includes('Too Many Requests')

      ) {

        throw new Error(`Rate limit exceeded for block ${blockNumber}`);

      }

      throw new Error(`Error analyzing block ${blockNumber}: ${error.message}`);

    }

  }


  async fetchAddressTransactions(address: string): Promise<void> {

    this.logger.log(`Starting fetch of transactions for address ${address}`);

    try {

      // Validate address

      if (!ethers.isAddress(address)) {

        throw new Error(`Invalid Ethereum address: ${address}`);

      }


      const minTimestamp = Math.floor((Date.now() - 30 * 24 * 60 * 60 * 1000) / 1000);

      const latestBlock = await this.provider.getBlockNumber();

      if (latestBlock <= 0) {

        throw new Error(`Invalid latest block number: ${latestBlock}`);

      }

      this.logger.debug(`Latest block: ${latestBlock}`);


      const transactions: any[] = [];


      const etherscanApiKey = this.configService.get('ETHERSCAN_FETCHER_API_KEY');

      if (!etherscanApiKey) {

        throw new Error('ETHERSCAN_FETCHER_API_KEY is not set in environment variables');

      }

      const etherscanUrl = 'https://api.etherscan.io/api';

      const BATCH_DELAY_MS = 1000; // Increased to 1000ms to avoid rate limits


      // Enhanced retry mechanism with exponential backoff

      const withRetry = async <T>(fn: () => Promise<T>, retries: number = 3): Promise<T> => {

        let lastError: any;

        for (let attempt = 1; attempt <= retries; attempt++) {

          try {

            return await fn();

          } catch (error) {

            lastError = error;

            const delayMs = 1000 * Math.pow(2, attempt - 1); // Exponential backoff: 1000, 2000, 4000ms

            if (attempt === retries || (!error.message.includes('rate limit') && !error.message.includes('NOTOK'))) {

              throw error;

            }

            this.logger.warn(`Attempt ${attempt} failed: ${error.message}. Retrying after ${delayMs}ms...`);

            await new Promise((resolve) => setTimeout(resolve, delayMs));

          }

        }

        throw lastError;

      };


      // Fetch ETH transactions

      let hasMore = true;

      let startBlock = latestBlock;

      while (hasMore) {

        const response = await withRetry(() =>

          this.limit(() =>

            axios.get(etherscanUrl, {

              params: {

                module: 'account',

                action: 'txlist',

                address,

                startblock: 0,

                endblock: startBlock,

                sort: 'desc',

                apikey: etherscanApiKey,

              },

            }),

          ),

        );


        if (response.data.status !== '1') {

          this.logger.error(`Etherscan API error: ${JSON.stringify(response.data)}`);

          throw new Error(`Etherscan API error: ${response.data.message || 'Unknown error'}`);

        }


        const txs = response.data.result;

        for (const tx of txs) {

          if (parseInt(tx.timeStamp) < minTimestamp) {

            hasMore = false;

            break;

          }

          transactions.push(tx);

          startBlock = parseInt(tx.blockNumber) - 1;

        }


        if (txs.length < 1000) {

          hasMore = false;

        }

        this.logger.debug(`ETH transactions batch processed, waiting ${BATCH_DELAY_MS}ms`);

        await new Promise((resolve) => setTimeout(resolve, BATCH_DELAY_MS));

      }


      // Fetch USDT transactions

      hasMore = true;

      startBlock = latestBlock;

      while (hasMore) {

        const response = await withRetry(() =>

          this.limit(() =>

            axios.get(etherscanUrl, {

              params: {

                module: 'account',

                action: 'tokentx',

                contractaddress: this.configService.get('ERC20_USDT_CONTRACT_ADDRESS') || '0xdAC17F958D2ee523a2206206994597C13D831ec7',

                address,

                startblock: 0,

                endblock: startBlock,

                sort: 'desc',

                apikey: etherscanApiKey,

              },

            }),

          ),

        );


        if (response.data.status !== '1') {

          this.logger.error(`Etherscan API error for tokentx: ${JSON.stringify(response.data)}`);

          throw new Error(`Etherscan API error for tokentx: ${response.data.message || 'Unknown error'}`);

        }


        const txs = response.data.result;

        for (const tx of txs) {

          if (parseInt(tx.timeStamp) < minTimestamp) {

            hasMore = false;

            break;

          }

          transactions.push(tx);

          startBlock = parseInt(tx.blockNumber) - 1;

        }


        if (txs.length < 1000) {

          hasMore = false;

        }

        this.logger.debug(`USDT transactions batch processed, waiting ${BATCH_DELAY_MS}ms`);

        await new Promise((resolve) => setTimeout(resolve, BATCH_DELAY_MS));

      }


      this.logger.log(`Fetched ${transactions.length} transactions for address ${address}`);


      const addressSet = await this.getMonitoredAddresses();

      const transactionsToSave: EthereumTransactionData[] = [];

      let ethTransactionsToSave = 0;

      let usdtTransactionsToSave = 0;


      for (const tx of transactions) {

        let transactionData: EthereumTransactionData = {

          _id: tx.hash,

          blockId: parseInt(tx.blockNumber),

          code: tx.isError === '0' ? 0 : 1,

          error: tx.isError === '0' ? undefined : 'FAILED',

          tx,

          timestamp: new Date(parseInt(tx.timeStamp) * 1000),

          chainId: 'mainnet',

          related: [],

          type: [],

          evts: 0,

          gasUsed: 0,

          fee: 0,

          amount: undefined,

        };


        if (!tx.contractAddress) {

          const from = tx.from.toLowerCase();

          const to = tx.to.toLowerCase();

          const value = BigInt(tx.value || '0');


          if (value < 0) {

            this.logger.warn(`Negative value detected for tx ${tx.hash}: ${value}, skipping`);

            continue;

          }


          if (addressSet.has(from) || addressSet.has(to)) {

            transactionData.related = [from, to];

            transactionData.type = ['ETH_TRANSFER'];

            transactionData.evts = 0;

            transactionData.amount = value.toString(); // Use amount field

            transactionData.gasUsed = parseInt(tx.gasUsed);

            transactionData.fee = Number(formatEther(BigInt(tx.gasUsed) * BigInt(tx.gasPrice)));


            const axmToAddress = await this.findAxmByEthAddress(to);

            if (axmToAddress && addressSet.has(to)) {

              await this.sendNotification(axmToAddress, 'tx_receive', value.toString(), parseInt(tx.timeStamp) * 1000, 'ETH');

            }

            const axmFromAddress = await this.findAxmByEthAddress(from);

            if (axmFromAddress && addressSet.has(from)) {

              await this.sendNotification(axmFromAddress, 'tx_send', value.toString(), parseInt(tx.timeStamp) * 1000, 'ETH');

            }


            transactionsToSave.push(transactionData);

            ethTransactionsToSave++;

          }

        } else if (tx.contractAddress.toLowerCase() === (this.configService.get('ERC20_USDT_CONTRACT_ADDRESS') || '0xdAC17F958D2ee523a2206206994597C13D831ec7').toLowerCase()) {

          const from = tx.from.toLowerCase();

          const to = tx.to.toLowerCase();

          const value = BigInt(tx.value || '0');


          if (!from.match(/^0x[a-fA-F0-9]{40}$/) || !to.match(/^0x[a-fA-F0-9]{40}$/)) {

            this.logger.warn(`Invalid address format for tx ${tx.hash}: from=${from}, to=${to}`);

            continue;

          }


          if (value < 0) {

            this.logger.warn(`Negative value detected for tx ${tx.hash}: ${value}, skipping`);

            continue;

          }


          if (addressSet.has(from) || addressSet.has(to)) {

            transactionData.related = [from, to];

            transactionData.type = ['USDT_TRANSFER'];

            transactionData.evts = 1;

            transactionData.amount = value.toString(); // Use amount field

            transactionData.gasUsed = parseInt(tx.gasUsed);

            transactionData.fee = Number(formatEther(BigInt(tx.gasUsed) * BigInt(tx.gasPrice)));


            const axmToAddress = await this.findAxmByEthAddress(to);

            if (axmToAddress && addressSet.has(to)) {

              await this.sendNotification(axmToAddress, 'tx_receive', value.toString(), parseInt(tx.timeStamp) * 1000, 'USDT');

            }

            const axmFromAddress = await this.findAxmByEthAddress(from);

            if (axmFromAddress && addressSet.has(from)) {

              await this.sendNotification(axmFromAddress, 'tx_send', value.toString(), parseInt(tx.timeStamp) * 1000, 'USDT');

            }


            transactionsToSave.push(transactionData);

            usdtTransactionsToSave++;

          }

        }

      }


      this.logger.log(

        `Processed ${transactions.length} transactions, ${ethTransactionsToSave} ETH and ${usdtTransactionsToSave} USDT relevant for saving`,

      );


      await this.saveTransactions(transactionsToSave);


      this.logger.log(`Completed fetch for address ${address}, saved ${transactionsToSave.length} transactions`);

    } catch (error) {

      this.logger.error(`Error fetching transactions for ${address}: ${error.message}`, error.stack);

      throw error;

    }

  }


  async watchEthereum(startBlock: string, endBlock: string): Promise<{ success: boolean; error?: string; lastProcessedBlock?: number }> {

    let fromBlock: number = 0;

    let toBlock: number = 0;


    try {

      if (startBlock === 'latest' || startBlock === 'current') {

        const lastProcessed = await this.processedEthBlocksModel

          .findOne({ chainId: 'mainnet' })

          .sort({ _id: -1 })

          .exec();

        fromBlock = lastProcessed ? lastProcessed._id + 1 : 0;

      } else {

        fromBlock = Number(startBlock);

      }


      if (endBlock === 'latest') {

        const block = await this.getLastBlock();

        toBlock = block.number;

        this.logger.log(`Processing to latest block: ${toBlock}`);

      } else {

        toBlock = Number(endBlock);

      }


      if (fromBlock > toBlock) {

        this.logger.log('No new blocks to process');

        return { success: true, lastProcessedBlock: fromBlock - 1 };

      }


      this.logger.log(`Processing blocks from ${fromBlock} to ${toBlock}`);


      let lastProcessedBlock = fromBlock - 1;


      for (let blockNum = fromBlock; blockNum <= toBlock; blockNum++) {

        const startTime = performance.now();

        try {

          await this.analyzeBlockTransactions(blockNum, true);

          lastProcessedBlock = blockNum;

        } catch (error) {

          const endTime = performance.now();

          this.logger.error(`Block ${blockNum} failed after ${((endTime - startTime) / 1000).toFixed(2)}s: ${error.message}`);

          if (error.message.includes('Rate limit exceeded')) {

            return { success: false, error: `Rate limit exceeded at block ${blockNum}`, lastProcessedBlock };

          }

          return { success: false, error: error.message, lastProcessedBlock };

        }

        const endTime = performance.now();

        this.logger.log(`Block ${blockNum} processed in ${((endTime - startTime) / 1000).toFixed(2)}s`);

      }


      return { success: true, lastProcessedBlock };

    } catch (error) {

      this.logger.error(`Error in watchEthereum: ${error.message}`);

      return { success: false, error: error.message, lastProcessedBlock: fromBlock - 1 };

    }

  }


  async getStats(): Promise<{ totalSupply: number; volume24h?: string; price?: number; marketCap?: string }> {

    let totalSupply = 0;

    let volume24h = '$0.00';

    let price = 0;

    let marketCap = '$0.00';


    try {

      const etherscanUrl = 'https://api.etherscan.io/api';

      const etherscanApiKey = this.configService.get('ETHERSCAN_API_KEY');

      this.logger.log(`Attempting to fetch total supply from ${etherscanUrl}`);

      const etherscanResponse = await axios.get(etherscanUrl, {

        params: {

          module: 'stats',

          action: 'ethsupply',

          apikey: etherscanApiKey,

        },

      });

      if (etherscanResponse.data.status === '1') {

        totalSupply = parseInt(etherscanResponse.data.result);

        this.logger.log(`Fetched totalSupply from Etherscan: ${formatEther(totalSupply)} ETH`);

      } else {

        throw new Error('Etherscan API returned no valid data');

      }

    } catch (etherscanError) {

      this.logger.warn(`Etherscan API failed: ${etherscanError.message}, using fallback estimation`);


      try {

        const blockCount = await this.provider.getBlockNumber();

        const initialSupply = 72_000_000_000_000_000_000_000;

        const blocksSinceEIP1559 = blockCount - 12965000;

        const rewardSupply = blockCount * 2_000_000_000_000_000_000;

        const burnEstimate = blocksSinceEIP1559 > 0 ? blocksSinceEIP1559 * 300_000_000_000_000_000 : 0;

        totalSupply = initialSupply + rewardSupply - burnEstimate;

        this.logger.log(`Estimated totalSupply from block ${blockCount}: ${formatEther(totalSupply)} ETH`);

      } catch (error) {

        this.logger.error(`Failed to estimate total supply: ${error.message}`);

      }

    }


    try {

      const coinGeckoUrl = 'https://api.coingecko.com/api/v3/coins/ethereum';

      const coinGeckoResponse = await axios.get(coinGeckoUrl, {

        params: {

          localization: false,

          tickers: false,

          market_data: true,

          community_data: false,

          developer_data: false,

          sparkline: false,

        },

      });

      const volume = coinGeckoResponse.data?.market_data?.total_volume?.usd || 0;

      volume24h = `$${volume.toFixed(2)}`;

      price = coinGeckoResponse.data?.market_data?.current_price?.usd || 0;

      marketCap = `$${(price * (totalSupply / 1_000_000_000_000_000_000)).toFixed(2)}`;

    } catch (coinGeckoError) {

      this.logger.error(`CoinGecko failed: ${coinGeckoError.message}`);

      const coinMarketCapUrl = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest';

      try {

        const coinMarketCapResponse = await axios.get(coinMarketCapUrl, {

          headers: { 'X-CMC_PRO_API_KEY': process.env.COINMARKETCAP_API_KEY },

          params: { symbol: 'ETH', convert: 'USD' },

        });

        const volume = coinMarketCapResponse.data?.data?.ETH?.quote?.USD?.volume_24h || 0;

        volume24h = `$${volume.toFixed(2)}`;

        price = coinMarketCapResponse.data?.data?.ETH?.quote?.USD?.price || 0;

        marketCap = `$${(price * (totalSupply / 1_000_000_000_000_000_000)).toFixed(2)}`;

      } catch (coinMarketCapError) {

        this.logger.error(`CoinMarketCap failed: ${coinMarketCapError.message}`);

      }

    }


    return {

      totalSupply,

      volume24h,

      price,

      marketCap,

    };

  }

}