@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/mirrorsBasic 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 builderAPI 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=trueModule 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.
