Skip to main content

Utilities and Services

The Standards SDK includes several utility classes and services that provide essential functionality across the SDK components.

Mirror Node Service​

The HederaMirrorNode class provides a streamlined interface for interacting with Hedera Mirror Nodes, offering methods to retrieve account information, topic messages, and pricing data.

Initialization​

import { HederaMirrorNode } from '@hashgraphonline/standards-sdk';
import { Logger } from '@hashgraphonline/standards-sdk';

const logger = new Logger({ module: 'MyApp', level: 'info' });
const mirrorNode = new HederaMirrorNode('testnet', logger);

Account Information​

Retrieve account information, including balances:

// Get account details including balance
const accountInfo = await mirrorNode.requestAccount('0.0.123456');
console.log(
'Account balance:',
accountInfo.balance.balance / 100_000_000,
'HBAR'
);

// Get account memo
const memo = await mirrorNode.getAccountMemo('0.0.123456');
console.log('Account memo:', memo);

// Get account public key
const publicKey = await mirrorNode.getPublicKey('0.0.123456');
console.log('Public key:', publicKey.toString());

Topic Information​

Retrieve topic information and messages:

// Get topic info
const topicInfo = await mirrorNode.getTopicInfo('0.0.123456');
console.log('Topic memo:', topicInfo.memo);

// Get topic fees
const fees = await mirrorNode.getTopicFees('0.0.123456');
console.log('Topic fees:', fees);

// Get topic messages
const messages = await mirrorNode.getTopicMessages('0.0.123456');
console.log('Topic messages:', messages);

HBAR Pricing​

Get current HBAR price for calculating fees:

// Get current HBAR price in USD
const hbarPrice = await mirrorNode.getHBARPrice(new Date());
console.log('Current HBAR price: $', hbarPrice);

// Calculate cost of HBAR needed for operations
const hbarNeeded = 5; // 5 HBAR
console.log('Cost in USD:', hbarNeeded * hbarPrice);

Logger​

The Logger class provides structured and level-based logging capabilities to help debug applications:

Basic Usage​

import { Logger } from '@hashgraphonline/standards-sdk';

// Create a logger instance
const logger = new Logger({
module: 'MyComponent',
level: 'debug', // trace, debug, info, warn, error, fatal
});

// Log at different levels
logger.debug('Detailed information for debugging');
logger.info('General information about application progress');
logger.warn('Warning situations that might cause issues');
logger.error('Error events that might still allow the application to continue');

Structured Logging​

The logger supports structured data in logs:

// Log with contextual data
logger.info('User authentication successful', {
userId: '12345',
loginTime: new Date().toISOString(),
ipAddress: '192.168.1.1',
});

// Error logging with details
try {
// Operation that might fail
} catch (error) {
logger.error('Failed to process transaction', {
transactionId: 'tx-12345',
error: error.message,
stack: error.stack,
});
}

Singleton Pattern​

The logger supports a singleton pattern for consistent logging across modules:

// In module 1
const logger1 = Logger.getInstance({ module: 'Module1', level: 'info' });

// In module 2
const logger2 = Logger.getInstance({ module: 'Module2' });

// Same logger instance is used and can be configured once

Progress Reporter​

The ProgressReporter class provides a standardized way to report progress for long-running operations:

Basic Usage​

import { ProgressReporter } from '@hashgraphonline/standards-sdk';

// Create a progress reporter
const reporter = new ProgressReporter({
module: 'FileUpload',
callback: (data) => {
console.log(`${data.stage}: ${data.message} (${data.progressPercent}%)`);
// Update UI with progress
updateProgressBar(data.progressPercent);
},
logger: myLogger, // Optional
});

// Report progress stages
reporter.preparing('Preparing file upload', 0);
reporter.submitting('Uploading file to server', 25);
reporter.confirming('Verifying upload integrity', 75);
reporter.completed('File upload complete');

// Or report progress manually
reporter.report({
stage: 'processing',
message: 'Analyzing file contents',
progressPercent: 50,
details: { fileSize: '2.5MB', fileType: 'image/jpeg' },
});

Sub-Progress Tracking​

Track progress of sub-tasks with percentage scaling:

// Create main progress reporter
const mainReporter = new ProgressReporter({
module: 'Registration',
callback: updateMainProgressUI,
});

// Create sub-reporter for a specific phase (20-50% of main progress)
const profileProgress = mainReporter.createSubProgress({
minPercent: 20,
maxPercent: 50,
logPrefix: 'Profile',
});

// Sub-reporter percentages are automatically scaled
profileProgress.preparing('Uploading profile picture', 0); // Reports 20% on main
profileProgress.submitting('Processing profile', 50); // Reports 35% on main
profileProgress.completed('Profile completed'); // Reports 50% on main

Error Handling​

Report failures with the progress reporter:

try {
// Long-running operation
reporter.preparing('Starting operation', 0);

// Simulated error
throw new Error('Network connection failed');
} catch (error) {
// Report failure with details
reporter.failed(`Operation failed: ${error.message}`, {
error: error.message,
timestamp: new Date().toISOString(),
});
}

API Reference​

Mirror Node Service​

class HederaMirrorNode {
constructor(network: 'mainnet' | 'testnet', logger?: Logger);

getBaseUrl(): string;

getPublicKey(accountId: string): Promise<PublicKey>;

getAccountMemo(accountId: string): Promise<string | null>;

getTopicInfo(topicId: string): Promise<TopicResponse>;

getTopicFees(topicId: string): Promise<CustomFees | null>;

getTopicMessages(topicId: string): Promise<HCSMessage[]>;

requestAccount(accountId: string): Promise<AccountResponse>;

checkKeyListAccess(
keyBytes: Buffer,
userPublicKey: PublicKey
): Promise<boolean>;

getHBARPrice(date: Date): Promise<number>;
}

Logger Class​

class Logger {
constructor(options: LoggerOptions);

static getInstance(options?: LoggerOptions): Logger;

debug(message: string, meta?: any): void;
info(message: string, meta?: any): void;
warn(message: string, meta?: any): void;
error(message: string, meta?: any): void;

setLevel(level: LogLevel): void;
getLevel(): LogLevel;
}

Progress Reporter​

class ProgressReporter {
constructor(options: ProgressReporterOptions);

preparing(stage: string, progressPercent: number): void;
submitting(stage: string, progressPercent: number): void;
confirming(stage: string, progressPercent: number): void;
completed(stage: string): void;
failed(stage: string, error: Error): void;
report(data: ProgressData): void;
createSubProgress(options: SubProgressOptions): ProgressReporter;
}

Integration Examples​

Custom Logging Integration​

import { Logger, LogLevel } from '@hashgraphonline/standards-sdk';

// Create a custom logging solution that integrates with an external service
function setupLogging() {
const logService = {
sendLog: (level, message, meta) => {
// Format timestamp
const timestamp = new Date().toISOString();

// Format metadata
const metaString = meta ? JSON.stringify(meta) : '';

// Prepare log entry
const logEntry = {
timestamp,
level: LogLevel[level],
message,
metadata: meta,
};

// In a real implementation, you might send this to a logging service
console.log(
`[${timestamp}] [${LogLevel[level]}] ${message} ${metaString}`
);

// Example: Send to external service
// fetch('https://logging-service.example.com/log', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(logEntry),
// }).catch(console.error);
},
};

// Configure the global logger instance
Logger.getInstance({
module: 'AppName',
level:
process.env.NODE_ENV === 'production' ? LogLevel.INFO : LogLevel.DEBUG,
callback: (level, message, meta) => {
logService.sendLog(level, message, meta);
},
});

console.log('Logging system initialized');
}

Best Practices​

Logging​

  1. Use Appropriate Log Levels: Use debug for detailed information, info for general operational messages, warn for potential issues, and error for problems requiring attention.

  2. Include Context: Add relevant metadata to log messages to provide context:

    logger.info('Processing transaction', {
    transactionId,
    accountId,
    timestamp: new Date().toISOString(),
    });
  3. Error Handling: Always log errors with their full stack traces:

    try {
    await someOperation();
    } catch (error) {
    logger.error('Operation failed', {
    error: error.message,
    stack: error.stack,
    });
    }

Mirror Node Usage​

  1. Caching: Implement caching for frequently accessed mirror node data:

    const cache = new Map();
    const CACHE_TTL = 60000; // 1 minute

    async function getCachedTopicInfo(topicId) {
    const cacheKey = `topic:${topicId}`;
    const now = Date.now();

    if (cache.has(cacheKey)) {
    const { data, timestamp } = cache.get(cacheKey);
    if (now - timestamp < CACHE_TTL) {
    return data;
    }
    }

    const info = await mirrorNode.getTopicInfo(topicId);
    cache.set(cacheKey, { data: info, timestamp: now });
    return info;
    }
  2. Rate Limiting: Implement rate limiting for mirror node requests:

    const pendingRequests = new Set();
    const MAX_CONCURRENT = 5;
    const RATE_LIMIT_DELAY = 100; // ms

    async function rateLimitedRequest(fn) {
    while (pendingRequests.size >= MAX_CONCURRENT) {
    await new Promise((resolve) => setTimeout(resolve, RATE_LIMIT_DELAY));
    }

    const requestId = Date.now() + Math.random();
    pendingRequests.add(requestId);

    try {
    return await fn();
    } finally {
    pendingRequests.delete(requestId);
    }
    }

    // Usage
    const topicInfo = await rateLimitedRequest(() =>
    mirrorNode.getTopicInfo(topicId)
    );
  3. Error Handling: Implement retries for transient mirror node errors:

    async function withRetry(fn, maxRetries = 3, delay = 1000) {
    let lastError;

    for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
    return await fn();
    } catch (error) {
    lastError = error;

    // Only retry on 5xx errors or network issues
    if (error.status && error.status < 500 && error.status !== 429) {
    throw error;
    }

    if (attempt < maxRetries - 1) {
    await new Promise((resolve) =>
    setTimeout(resolve, delay * Math.pow(2, attempt))
    );
    }
    }
    }

    throw lastError;
    }

    // Usage
    const messages = await withRetry(() => mirrorNode.getTopicMessages(topicId));