npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, πŸ‘‹, I’m Ryan HefnerΒ  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you πŸ™

Β© 2026 – Pkg Stats / Ryan Hefner

@hsuite/mirrors

v2.0.7

Published

> πŸ” **Comprehensive NestJS module for Hedera Mirror Node API integration with caching and error handling**

Downloads

26

Readme

@hsuite/mirrors - Hedera Mirror Node Integration

πŸ” Comprehensive NestJS module for Hedera Mirror Node API integration with caching and error handling

Advanced mirror node integration providing efficient access to Hedera transaction data, account information, and network statistics with built-in caching, retry logic, and comprehensive error handling.


Table of Contents


Quick Start

Installation

npm install @hsuite/mirrors

Basic Setup

import { MirrorsModule } from '@hsuite/mirrors';

@Module({
  imports: [
    MirrorsModule.forRootAsync({
      useFactory: () => ({
        baseUrl: 'https://testnet.mirrornode.hedera.com',
        maxRetries: 3,
        timeout: 10000,
        enableCaching: true,
        cacheTime: 300000 // 5 minutes
      })
    })
  ]
})
export class AppModule {}

Basic Usage

@Injectable()
export class MirrorService {
  constructor(private mirrorsService: MirrorsService) {}

  async getAccountInfo(accountId: string) {
    return await this.mirrorsService.getAccount(accountId);
  }

  async getTransactionHistory(accountId: string) {
    return await this.mirrorsService.getTransactions(accountId);
  }
}

Architecture

Core Services

🏦 AccountsService

  • Account Information - Complete account details and metadata
  • Balance Queries - Real-time balance tracking
  • Allowances - Crypto, NFT, and token allowance management
  • Token Relationships - Token associations and holdings
  • NFT Ownership - Non-fungible token inventory
  • Staking Rewards - Reward tracking and history

πŸ’³ TransactionsService

  • Transaction Lookup - Individual transaction details by ID
  • Transaction History - Filtered and paginated transaction lists
  • Status Tracking - Transaction success/failure monitoring
  • Type Filtering - Filter by transaction types and results

πŸͺ™ TokensService

  • Token Information - Token metadata and details
  • Balance Distribution - Token holder analytics
  • NFT Metadata - Non-fungible token details
  • Transaction History - Token-specific transaction tracking

🌐 NetworkService

  • Exchange Rates - Current HBAR to USD rates
  • Network Fees - Transaction cost estimation
  • Node Information - Consensus node details
  • Stake Information - Network staking data
  • Supply Tracking - HBAR supply metrics

βš–οΈ BalancesService

  • Balance Snapshots - Historical balance data
  • Balance Filtering - Query by threshold and criteria
  • Account Analytics - Balance distribution insights

🎯 AirdropsService

  • Outstanding Airdrops - Pending airdrop queries
  • Airdrop Status - Track airdrop distribution
  • Token Airdrops - Fungible and non-fungible airdrops

Module Structure

src/
β”œβ”€β”€ mirrors.service.ts          # Base service with HTTP client
β”œβ”€β”€ mirrors.controller.ts       # Main controller
β”œβ”€β”€ mirrors.module.ts          # Module configuration
β”œβ”€β”€ accounts/
β”‚   β”œβ”€β”€ accounts.service.ts    # Account operations
β”‚   └── accounts.controller.ts # Account endpoints
β”œβ”€β”€ transactions/
β”‚   β”œβ”€β”€ transactions.service.ts    # Transaction operations
β”‚   └── transactions.controller.ts # Transaction endpoints
β”œβ”€β”€ tokens/
β”‚   β”œβ”€β”€ tokens.service.ts      # Token operations
β”‚   └── tokens.controller.ts   # Token endpoints
β”œβ”€β”€ network/
β”‚   β”œβ”€β”€ network.service.ts     # Network operations
β”‚   └── network.controller.ts  # Network endpoints
β”œβ”€β”€ balances/
β”‚   β”œβ”€β”€ balances.service.ts    # Balance operations
β”‚   └── balances.controller.ts # Balance endpoints
β”œβ”€β”€ airdrops/
β”‚   β”œβ”€β”€ airdrops.service.ts    # Airdrop operations
β”‚   └── airdrops.controller.ts # Airdrop endpoints
β”œβ”€β”€ guards/
β”‚   └── role.guard.ts          # Access control
└── query-builder.ts           # Query parameter builder

API Reference

AccountsService

Core Methods

getAccountInfo(params): Promise<IAccountInfo>

  • Retrieves detailed account information
  • Parameters: Account ID, alias, or EVM address
  • Returns: Complete account details

getNftAllowances(params): Promise<INftAllowances>

  • Gets NFT allowances for an account
  • Parameters: Account ID, token filters, pagination
  • Returns: NFT allowance details

getTokenAllowances(params): Promise<ITokenAllowances>

  • Retrieves token allowances for an account
  • Parameters: Account ID, token filters, pagination
  • Returns: Token allowance information

getTokenRelationships(params): Promise<ITokenRelationships>

  • Gets token associations for an account
  • Parameters: Account ID, token filters, pagination
  • Returns: Token relationship data

TransactionsService

Core Methods

getTransactionById(transactionId): Promise<ITransaction>

  • Retrieves individual transaction by ID
  • Parameters: Transaction ID with format validation
  • Returns: Complete transaction details

getTransactions(params): Promise<ITransactionResponse>

  • Gets filtered list of transactions
  • Parameters: Filters, pagination, sorting options
  • Returns: Paginated transaction list

TokensService

Core Methods

getTokens(params): Promise<ITokensResponse>

  • Retrieves filtered list of tokens
  • Parameters: Search criteria, pagination, sorting
  • Returns: Token list with metadata

getTokenInfo(tokenId): Promise<ITokenInfo>

  • Gets detailed token information
  • Parameters: Token ID
  • Returns: Complete token details

getTokenBalances(params): Promise<ITokenBalances>

  • Retrieves token balance distribution
  • Parameters: Token ID, balance filters, pagination
  • Returns: Token holder balances

NetworkService

Core Methods

getExchangeRate(): Promise<IExchangeRate>

  • Gets current HBAR exchange rate
  • Returns: Exchange rate information

getFees(): Promise<INetworkFees>

  • Retrieves current network fees
  • Returns: Fee schedule information

getNodes(params): Promise<INetworkNodes>

  • Gets network node information
  • Parameters: Node filters, pagination
  • Returns: Consensus node details

getStake(): Promise<IStakeInfo>

  • Retrieves network stake information
  • Returns: Staking data and rewards

BalancesService

Core Methods

getBalances(params): Promise<IBalanceResponse>

  • Gets account balances with filtering
  • Parameters: Account filters, balance thresholds, pagination
  • Returns: Balance snapshot data

Query Parameters

All services support standardized query parameters:

interface QueryParams {
  limit?: number;           // Result limit (default: 25)
  order?: 'asc' | 'desc';  // Sort order (default: 'desc')
  timestamp?: string;       // Timestamp filter
}

interface AccountQueryParams extends QueryParams {
  'account.id'?: string;           // Account ID filter
  'account.balance'?: string;      // Balance filter
  'account.publickey'?: string;    // Public key filter
}

interface TokenQueryParams extends QueryParams {
  'token.id'?: string;        // Token ID filter
  'account.id'?: string;      // Account filter
  'balance'?: string;         // Balance threshold
}

πŸ“– Guides

Mirror Node Setup Guide

Configure mirror node connections and API keys. Comprehensive setup guide covering mirror node configuration, API authentication, network settings, endpoint management, and enterprise-grade mirror node integration for reliable blockchain data access.

Query Optimization Guide

Optimize queries for performance and cost efficiency. Advanced optimization guide covering query patterns, performance tuning, caching strategies, rate limiting, and enterprise query optimization for high-performance blockchain data retrieval.

Real-time Data Guide

Implement real-time data streaming and updates. Detailed implementation guide covering WebSocket connections, event streaming, real-time notifications, data synchronization, and enterprise real-time blockchain data processing systems.


Examples

Account Information and Analysis

import { AccountsService, TokensService } from '@hsuite/mirrors';

@Injectable()
export class AccountAnalyticsService {
  constructor(
    private accountsService: AccountsService,
    private tokensService: TokensService
  ) {}

  async getCompleteAccountProfile(accountId: string) {
    try {
      // Get basic account information
      const accountInfo = await this.accountsService.getAccountInfo({
        idOrAliasOrEvmAddress: accountId
      });

      // Get token relationships
      const tokenRelationships = await this.accountsService.getTokenRelationships({
        idOrAliasOrEvmAddress: accountId,
        limit: 100,
        order: 'desc'
      });

      // Get NFT allowances
      const nftAllowances = await this.accountsService.getNftAllowances({
        idOrAliasOrEvmAddress: accountId,
        limit: 50
      });

      // Get token allowances
      const tokenAllowances = await this.accountsService.getTokenAllowances({
        idOrAliasOrEvmAddress: accountId,
        limit: 50
      });

      return {
        account: accountInfo,
        tokenCount: tokenRelationships.tokens?.length || 0,
        nftAllowanceCount: nftAllowances.allowances?.length || 0,
        tokenAllowanceCount: tokenAllowances.allowances?.length || 0,
        totalBalance: accountInfo.balance,
        createdTimestamp: accountInfo.created_timestamp,
        isDeleted: accountInfo.deleted,
        stakingInfo: {
          declineReward: accountInfo.decline_reward,
          stakedAccountId: accountInfo.staked_account_id,
          stakedNodeId: accountInfo.staked_node_id
        }
      };
    } catch (error) {
      throw new Error(`Failed to get account profile: ${error.message}`);
    }
  }

  async getAccountTokenPortfolio(accountId: string) {
    const tokenRelationships = await this.accountsService.getTokenRelationships({
      idOrAliasOrEvmAddress: accountId,
      limit: 100
    });

    const portfolio = [];

    for (const token of tokenRelationships.tokens || []) {
      try {
        const tokenInfo = await this.tokensService.getTokenInfo(token.token_id);
        
        portfolio.push({
          tokenId: token.token_id,
          balance: token.balance,
          automaticAssociation: token.automatic_association,
          freezeStatus: token.freeze_status,
          kycStatus: token.kyc_status,
          tokenInfo: {
            name: tokenInfo.name,
            symbol: tokenInfo.symbol,
            decimals: tokenInfo.decimals,
            totalSupply: tokenInfo.total_supply,
            type: tokenInfo.type
          }
        });
      } catch (error) {
        console.warn(`Failed to get token info for ${token.token_id}:`, error);
      }
    }

    return {
      accountId,
      totalTokens: portfolio.length,
      portfolio: portfolio.sort((a, b) => 
        parseInt(b.balance) - parseInt(a.balance)
      )
    };
  }
}

Transaction Monitoring and Analysis

import { TransactionsService } from '@hsuite/mirrors';

@Injectable()
export class TransactionMonitoringService {
  constructor(private transactionsService: TransactionsService) {}

  async getAccountTransactionHistory(
    accountId: string, 
    options: {
      days?: number;
      transactionType?: string;
      limit?: number;
    } = {}
  ) {
    const { days = 30, transactionType, limit = 100 } = options;
    
    // Calculate timestamp for filtering
    const fromTimestamp = new Date();
    fromTimestamp.setDate(fromTimestamp.getDate() - days);
    
    try {
      const transactions = await this.transactionsService.getTransactions({
        accountId,
        timestamp: `gte:${fromTimestamp.toISOString()}`,
        transactionType,
        limit,
        order: 'desc'
      });

      // Analyze transaction patterns
      const analysis = this.analyzeTransactions(transactions.transactions || []);

      return {
        accountId,
        period: `${days} days`,
        totalTransactions: transactions.transactions?.length || 0,
        analysis,
        transactions: transactions.transactions
      };
    } catch (error) {
      throw new Error(`Failed to get transaction history: ${error.message}`);
    }
  }

  private analyzeTransactions(transactions: any[]) {
    const typeCount = {};
    const statusCount = { SUCCESS: 0, FAIL: 0 };
    let totalFees = 0;
    const dailyActivity = {};

    transactions.forEach(tx => {
      // Count by type
      const type = tx.name || 'UNKNOWN';
      typeCount[type] = (typeCount[type] || 0) + 1;

      // Count by status
      const result = tx.result;
      if (result === 'SUCCESS') statusCount.SUCCESS++;
      else statusCount.FAIL++;

      // Sum fees
      totalFees += parseInt(tx.charged_tx_fee || '0');

      // Daily activity
      const date = new Date(tx.consensus_timestamp * 1000).toDateString();
      dailyActivity[date] = (dailyActivity[date] || 0) + 1;
    });

    return {
      byType: typeCount,
      byStatus: statusCount,
      successRate: (statusCount.SUCCESS / transactions.length * 100).toFixed(2) + '%',
      totalFeesHbar: (totalFees / 100000000).toFixed(8), // Convert tinybar to HBAR
      dailyActivity,
      averagePerDay: (transactions.length / Object.keys(dailyActivity).length).toFixed(1)
    };
  }

  async monitorTransactionStatus(transactionId: string) {
    try {
      const transaction = await this.transactionsService.getTransactionById(transactionId);

      return {
        transactionId,
        status: transaction.result,
        consensusTimestamp: transaction.consensus_timestamp,
        chargedFee: transaction.charged_tx_fee,
        type: transaction.name,
        isSuccess: transaction.result === 'SUCCESS',
        details: {
          entityId: transaction.entity_id,
          validStartTimestamp: transaction.valid_start_timestamp,
          maxFee: transaction.max_fee,
          memo: transaction.memo_base64
        }
      };
    } catch (error) {
      throw new Error(`Failed to get transaction status: ${error.message}`);
    }
  }
}

Token Analytics and Tracking

import { TokensService, AccountsService } from '@hsuite/mirrors';

@Injectable()
export class TokenAnalyticsService {
  constructor(
    private tokensService: TokensService,
    private accountsService: AccountsService
  ) {}

  async getTokenAnalytics(tokenId: string) {
    try {
      // Get token information
      const tokenInfo = await this.tokensService.getTokenInfo(tokenId);

      // Get token balance distribution
      const balances = await this.tokensService.getTokenBalances({
        tokenId,
        limit: 1000,
        order: 'desc'
      });

      // Analyze distribution
      const analysis = this.analyzeTokenDistribution(balances.balances || []);

      return {
        token: {
          id: tokenInfo.token_id,
          name: tokenInfo.name,
          symbol: tokenInfo.symbol,
          decimals: tokenInfo.decimals,
          type: tokenInfo.type,
          totalSupply: tokenInfo.total_supply,
          treasuryAccountId: tokenInfo.treasury_account_id,
          createdTimestamp: tokenInfo.created_timestamp
        },
        distribution: {
          totalHolders: balances.balances?.length || 0,
          ...analysis
        },
        balances: balances.balances
      };
    } catch (error) {
      throw new Error(`Failed to get token analytics: ${error.message}`);
    }
  }

  private analyzeTokenDistribution(balances: any[]) {
    if (!balances.length) return {};

    const balanceValues = balances.map(b => parseInt(b.balance));
    const totalBalance = balanceValues.reduce((sum, balance) => sum + balance, 0);

    // Calculate distribution metrics
    const top10Holdings = balanceValues.slice(0, 10).reduce((sum, balance) => sum + balance, 0);
    const top100Holdings = balanceValues.slice(0, 100).reduce((sum, balance) => sum + balance, 0);

    return {
      concentration: {
        top10Percentage: ((top10Holdings / totalBalance) * 100).toFixed(2) + '%',
        top100Percentage: ((top100Holdings / totalBalance) * 100).toFixed(2) + '%'
      },
      statistics: {
        maxBalance: Math.max(...balanceValues),
        minBalance: Math.min(...balanceValues),
        averageBalance: (totalBalance / balances.length).toFixed(0),
        medianBalance: this.calculateMedian(balanceValues)
      },
      totalCirculating: totalBalance
    };
  }

  private calculateMedian(values: number[]): number {
    const sorted = [...values].sort((a, b) => a - b);
    const mid = Math.floor(sorted.length / 2);
    return sorted.length % 2 === 0 
      ? (sorted[mid - 1] + sorted[mid]) / 2 
      : sorted[mid];
  }

  async trackTokenTransfers(tokenId: string, days: number = 7) {
    const fromTimestamp = new Date();
    fromTimestamp.setDate(fromTimestamp.getDate() - days);

    try {
      const transactions = await this.transactionsService.getTransactions({
        timestamp: `gte:${fromTimestamp.toISOString()}`,
        transactionType: 'CRYPTOTRANSFER',
        limit: 1000,
        order: 'desc'
      });

      // Filter for token-specific transfers
      const tokenTransfers = (transactions.transactions || []).filter(tx => 
        tx.token_transfers?.some(transfer => transfer.token_id === tokenId)
      );

      const transferAnalysis = this.analyzeTokenTransfers(tokenTransfers, tokenId);

      return {
        tokenId,
        period: `${days} days`,
        totalTransfers: tokenTransfers.length,
        analysis: transferAnalysis,
        recentTransfers: tokenTransfers.slice(0, 50)
      };
    } catch (error) {
      throw new Error(`Failed to track token transfers: ${error.message}`);
    }
  }

  private analyzeTokenTransfers(transactions: any[], tokenId: string) {
    let totalVolume = 0;
    const uniqueAccounts = new Set();
    const dailyVolume = {};

    transactions.forEach(tx => {
      const tokenTransfers = tx.token_transfers?.filter(t => t.token_id === tokenId) || [];
      
      tokenTransfers.forEach(transfer => {
        totalVolume += Math.abs(parseInt(transfer.amount));
        uniqueAccounts.add(transfer.account);

        const date = new Date(tx.consensus_timestamp * 1000).toDateString();
        dailyVolume[date] = (dailyVolume[date] || 0) + Math.abs(parseInt(transfer.amount));
      });
    });

    return {
      totalVolume,
      uniqueAccounts: uniqueAccounts.size,
      averageTransferSize: transactions.length > 0 ? (totalVolume / transactions.length).toFixed(0) : '0',
      dailyVolume,
      peakDay: Object.entries(dailyVolume).reduce((a, b) => a[1] > b[1] ? a : b)?.[0]
    };
  }
}

Network Monitoring Dashboard

import { NetworkService, BalancesService } from '@hsuite/mirrors';

@Injectable()
export class NetworkDashboardService {
  constructor(
    private networkService: NetworkService,
    private balancesService: BalancesService
  ) {}

  async getNetworkOverview() {
    try {
      // Get all network data in parallel
      const [exchangeRate, fees, nodes, stake, balances] = await Promise.allSettled([
        this.networkService.getExchangeRate(),
        this.networkService.getFees(),
        this.networkService.getNodes({ limit: 100 }),
        this.networkService.getStake(),
        this.balancesService.getBalances({ limit: 1000 })
      ]);

      return {
        timestamp: new Date().toISOString(),
        network: {
          exchangeRate: exchangeRate.status === 'fulfilled' ? exchangeRate.value : null,
          fees: fees.status === 'fulfilled' ? fees.value : null,
          nodes: nodes.status === 'fulfilled' ? this.analyzeNodes(nodes.value) : null,
          stake: stake.status === 'fulfilled' ? stake.value : null,
          balances: balances.status === 'fulfilled' ? this.analyzeBalances(balances.value) : null
        },
        errors: this.collectErrors([exchangeRate, fees, nodes, stake, balances])
      };
    } catch (error) {
      throw new Error(`Failed to get network overview: ${error.message}`);
    }
  }

  private analyzeNodes(nodesData: any) {
    if (!nodesData.nodes) return null;

    const totalNodes = nodesData.nodes.length;
    const activeNodes = nodesData.nodes.filter(node => !node.deleted).length;

    return {
      total: totalNodes,
      active: activeNodes,
      deleted: totalNodes - activeNodes,
      stakeDistribution: nodesData.nodes.map(node => ({
        nodeId: node.node_id,
        accountId: node.node_account_id,
        description: node.description,
        stake: node.stake,
        stakeNotRewarded: node.stake_not_rewarded,
        stakeRewarded: node.stake_rewarded
      }))
    };
  }

  private analyzeBalances(balancesData: any) {
    if (!balancesData.balances) return null;

    const balances = balancesData.balances.map(b => parseInt(b.balance));
    const totalAccounts = balances.length;
    const totalBalance = balances.reduce((sum, balance) => sum + balance, 0);

    return {
      totalAccounts,
      totalBalance: (totalBalance / 100000000).toFixed(8) + ' HBAR', // Convert to HBAR
      averageBalance: ((totalBalance / totalAccounts) / 100000000).toFixed(8) + ' HBAR',
      distribution: {
        millionaires: balances.filter(b => b >= 1000000 * 100000000).length,
        thousandaires: balances.filter(b => b >= 1000 * 100000000).length,
        hundreds: balances.filter(b => b >= 100 * 100000000).length
      }
    };
  }

  private collectErrors(results: PromiseSettledResult<any>[]): string[] {
    return results
      .filter(result => result.status === 'rejected')
      .map(result => (result as PromiseRejectedResult).reason?.message || 'Unknown error');
  }

  async getNetworkHealth() {
    const start = Date.now();

    try {
      // Test basic connectivity
      const exchangeRate = await this.networkService.getExchangeRate();
      const responseTime = Date.now() - start;

      return {
        status: 'healthy',
        responseTime: responseTime + 'ms',
        timestamp: new Date().toISOString(),
        currentRate: exchangeRate.current_rate,
        nextUpdate: exchangeRate.next_update
      };
    } catch (error) {
      return {
        status: 'unhealthy',
        responseTime: (Date.now() - start) + 'ms',
        timestamp: new Date().toISOString(),
        error: error.message
      };
    }
  }
}

Real-time Data Polling Service

import { Injectable, OnDestroy } from '@nestjs/common';
import { Interval } from '@nestjs/schedule';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { TransactionsService, AccountsService } from '@hsuite/mirrors';

@Injectable()
export class RealTimeDataService implements OnDestroy {
  private monitoredAccounts = new Set<string>();
  private lastProcessedTimestamp: string = '';

  constructor(
    private transactionsService: TransactionsService,
    private accountsService: AccountsService,
    private eventEmitter: EventEmitter2
  ) {
    this.initializeTimestamp();
  }

  private async initializeTimestamp() {
    try {
      const recentTx = await this.transactionsService.getTransactions({
        limit: 1,
        order: 'desc'
      });
      
      if (recentTx.transactions?.[0]) {
        this.lastProcessedTimestamp = recentTx.transactions[0].consensus_timestamp;
      }
    } catch (error) {
      console.error('Failed to initialize timestamp:', error);
    }
  }

  addAccountToMonitor(accountId: string) {
    this.monitoredAccounts.add(accountId);
    console.log(`Now monitoring account: ${accountId}`);
  }

  removeAccountFromMonitor(accountId: string) {
    this.monitoredAccounts.delete(accountId);
    console.log(`Stopped monitoring account: ${accountId}`);
  }

  @Interval(5000) // Poll every 5 seconds
  async pollForNewTransactions() {
    if (this.monitoredAccounts.size === 0) return;

    try {
      const newTransactions = await this.transactionsService.getTransactions({
        timestamp: `gt:${this.lastProcessedTimestamp}`,
        limit: 100,
        order: 'asc'
      });

      if (newTransactions.transactions?.length) {
        for (const transaction of newTransactions.transactions) {
          this.processTransaction(transaction);
        }

        // Update last processed timestamp
        const lastTx = newTransactions.transactions[newTransactions.transactions.length - 1];
        this.lastProcessedTimestamp = lastTx.consensus_timestamp;
      }
    } catch (error) {
      console.error('Error polling for new transactions:', error);
    }
  }

  private processTransaction(transaction: any) {
    // Check if transaction involves any monitored accounts
    const involvedAccounts = this.getInvolvedAccounts(transaction);
    const monitoredInvolved = involvedAccounts.filter(accountId => 
      this.monitoredAccounts.has(accountId)
    );

    if (monitoredInvolved.length > 0) {
      this.eventEmitter.emit('transaction.new', {
        transaction,
        involvedAccounts: monitoredInvolved,
        timestamp: new Date().toISOString()
      });

      console.log(`New transaction ${transaction.transaction_id} involves monitored accounts:`, monitoredInvolved);
    }
  }

  private getInvolvedAccounts(transaction: any): string[] {
    const accounts = new Set<string>();

    // Add transaction initiator
    if (transaction.entity_id) {
      accounts.add(transaction.entity_id);
    }

    // Add crypto transfer participants
    if (transaction.transfers) {
      transaction.transfers.forEach(transfer => {
        accounts.add(transfer.account);
      });
    }

    // Add token transfer participants
    if (transaction.token_transfers) {
      transaction.token_transfers.forEach(transfer => {
        accounts.add(transfer.account);
      });
    }

    // Add NFT transfer participants
    if (transaction.nft_transfers) {
      transaction.nft_transfers.forEach(transfer => {
        accounts.add(transfer.receiver_account_id);
        accounts.add(transfer.sender_account_id);
      });
    }

    return Array.from(accounts);
  }

  onDestroy() {
    this.monitoredAccounts.clear();
  }
}

Integration

Required Dependencies

{
  "@nestjs/axios": "^3.0.0",
  "@hsuite/hashgraph-types": "^2.0.3",
  "@hsuite/smart-config": "^2.1.1",
  "@hsuite/auth-types": "^2.1.2",
  "@hsuite/nestjs-swagger": "^1.0.3",
  "axios": "^1.6.0",
  "lodash": "^4.17.21"
}

Environment Variables

# Mirror Node Configuration
HEDERA_MIRROR_NODE_URL=https://testnet.mirrornode.hedera.com
MIRROR_NODE_API_KEY=your_api_key_here

# Request Configuration
MIRROR_NODE_TIMEOUT=30000
MIRROR_NODE_RETRY_ATTEMPTS=3
MIRROR_NODE_RATE_LIMIT=100

# Authentication
MIRROR_ACCESS_ROLES=smart-node,admin
MIRROR_ENABLE_AUTH=true

Module Configuration

import { MirrorsModule } from '@hsuite/mirrors';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    MirrorsModule.forRootAsync({
      imports: [ConfigModule, HttpModule],
      useFactory: (configService: ConfigService) => ({
        mirrorNode: {
          url: configService.get('HEDERA_MIRROR_NODE_URL'),
          apiKey: configService.get('MIRROR_NODE_API_KEY'),
          timeout: configService.get('MIRROR_NODE_TIMEOUT', 30000),
          retryAttempts: configService.get('MIRROR_NODE_RETRY_ATTEMPTS', 3)
        },
        auth: {
          enabled: configService.get('MIRROR_ENABLE_AUTH', true),
          roles: configService.get('MIRROR_ACCESS_ROLES', 'smart-node').split(',')
        },
        rateLimit: {
          requestsPerSecond: configService.get('MIRROR_NODE_RATE_LIMIT', 100)
        }
      }),
      inject: [ConfigService]
    })
  ]
})
export class AppModule {}

Integration with HSuite Ecosystem

// Complete integration with other HSuite modules
@Module({
  imports: [
    HashgraphModule,
    MirrorsModule,
    ValidatorsModule,
    SmartConfigModule,
    EventEmitterModule.forRoot()
  ]
})
export class BlockchainModule {}

@Injectable()
export class ComprehensiveBlockchainService {
  constructor(
    private hashgraphService: HashgraphAccountsService,
    private accountsService: AccountsService,
    private transactionsService: TransactionsService,
    private validatorsService: ValidatorsService
  ) {}

  async createAndMonitorAccount(accountData: any) {
    // 1. Create account using hashgraph service
    const createTx = await this.hashgraphService.createAccount(
      accountData.session,
      accountData.payload
    );

    // 2. Execute transaction and get ID
    const txResult = await this.executeTransaction(createTx);

    // 3. Monitor transaction status via mirror node
    const txStatus = await this.waitForTransactionConfirmation(txResult.transactionId);

    // 4. Get complete account info from mirror node
    if (txStatus.result === 'SUCCESS') {
      const accountInfo = await this.accountsService.getAccountInfo({
        idOrAliasOrEvmAddress: txStatus.entity_id
      });

      return {
        success: true,
        transactionId: txResult.transactionId,
        accountId: txStatus.entity_id,
        accountInfo: accountInfo,
        confirmationTime: txStatus.consensus_timestamp
      };
    }

    throw new Error(`Account creation failed: ${txStatus.result}`);
  }

  private async waitForTransactionConfirmation(transactionId: string, maxAttempts = 10) {
    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      try {
        const transaction = await this.transactionsService.getTransactionById(transactionId);
        if (transaction.result) {
          return transaction;
        }
      } catch (error) {
        // Transaction not yet available, wait and retry
      }

      await new Promise(resolve => setTimeout(resolve, 2000));
    }

    throw new Error(`Transaction confirmation timeout: ${transactionId}`);
  }
}

πŸ” Comprehensive Coverage: Complete access to all Hedera Mirror Node APIs with type-safe interfaces and robust error handling.

⚑ Performance Optimized: Advanced caching, query optimization, and pagination for enterprise-scale applications.

πŸ›‘οΈ Enterprise Security: Role-based access control, API key management, and request validation for secure data access.