@satianurag/hiero-mirror-client
v0.3.0
Published
Standalone TypeScript client for the Hedera/Hiero Mirror Node REST API
Maintainers
Readme
@satianurag/hiero-mirror-client
Standalone TypeScript client for the Hedera / Hiero Mirror Node REST API.
Built for precision, type safety, and developer ergonomics — no silent data corruption, no manual pagination, no guesswork.
Features
- Full API coverage — 49 methods across 9 resource groups
- Safe JSON parsing — int64 values preserved as strings (no precision loss)
- Three pagination patterns —
await,for await...of,.pages() - Adaptive HCS streaming — topic message polling with auto-backoff
- ETag caching — automatic conditional requests with
If-None-Match - Typed errors — structured error hierarchy (
HieroNotFoundError,HieroRateLimitError, etc.) - Dual output — ESM + CJS with full
.d.tsdeclarations - Zero platform dependencies — works in Node.js 22+, Deno, Bun, and browsers
- HBAR conversion utilities —
tinybarToHbar,hbarToTinybar,formatHbar - Scheduled transaction helpers — poll execution, track signatures, filter by account
- Transaction confirmation poller —
waitFor()bridges consensus-to-mirror-node lag - MCP tool descriptors — AI agent integration via Model Context Protocol
Installation
npm install @satianurag/hiero-mirror-clientQuick Start
import { MirrorNodeClient } from '@satianurag/hiero-mirror-client';
const client = new MirrorNodeClient({ network: 'mainnet' });
// Get an account
const account = await client.accounts.get('0.0.800');
console.log(account.balance);
// List tokens (first page)
const page = await client.tokens.list({ limit: 10 });
console.log(page.data);Pagination
Three ergonomic patterns for consuming paginated results:
// 1. Await — get the first page
const firstPage = await client.accounts.list({ limit: 25 });
console.log(firstPage.data); // AccountSummary[]
console.log(firstPage.links.next); // cursor or null
// 2. Auto-paginate items
for await (const account of client.accounts.list()) {
console.log(account.account);
}
// 3. Page-by-page
for await (const page of client.accounts.list({ limit: 100 }).pages()) {
console.log(`Got ${page.data.length} accounts`);
}Topic Streaming
Subscribe to Hedera Consensus Service messages with adaptive polling:
const controller = new AbortController();
for await (const msg of client.topics.stream('0.0.12345', {
signal: controller.signal,
})) {
console.log(msg.consensus_timestamp, msg.message);
}
// Cancel anytime
controller.abort();Error Handling
All errors extend HieroError for easy pattern matching:
import {
HieroNotFoundError,
HieroRateLimitError,
HieroValidationError,
} from '@satianurag/hiero-mirror-client';
try {
await client.accounts.get('0.0.999999999');
} catch (error) {
if (error instanceof HieroNotFoundError) {
console.log('Account not found');
} else if (error instanceof HieroRateLimitError) {
console.log(`Rate limited, retry after ${error.retryAfter}ms`);
}
}Configuration
const client = new MirrorNodeClient({
// Network preset (or use baseUrl for custom)
network: 'testnet', // 'mainnet' | 'testnet' | 'previewnet'
// baseUrl: 'https://custom-mirror.example.com',
timeout: 30_000, // Request timeout (default: 30s)
maxRetries: 2, // Retry count (default: 2)
rateLimitRps: 50, // Rate limit (default: 50 req/s)
logger: console, // Optional logger (silent by default)
});Network URLs
| Network | URL |
|---------|-----|
| mainnet | https://mainnet-public.mirrornode.hedera.com |
| testnet | https://testnet.mirrornode.hedera.com |
| previewnet | https://previewnet.mirrornode.hedera.com |
Resources
| Resource | Methods | Key Operations |
|----------|---------|----------------|
| accounts | 10 | list, get, NFTs, tokens, rewards, allowances, airdrops |
| balances | 3 | list, getForAccount, getTokenBalance |
| blocks | 2 | list, get |
| contracts | 12 | list, get, call (POST), results, actions, logs, state, opcodes |
| network | 6 | exchangeRate, fees, estimateFees (POST), nodes, stake, supply |
| schedules | 2 | list, get |
| tokens | 6 | list, get, balances, NFTs, NFT transactions |
| topics | 5 | get, messages, stream |
| transactions | 3 | list, get, waitFor |
Transaction Confirmation Poller
Bridge the consensus-to-mirror-node ingestion lag (typically 3-7s):
// Poll until the transaction appears on the mirror node
const tx = await client.transactions.waitFor(
'[email protected]',
{ timeout: 15_000 },
);
console.log(tx.result, tx.consensus_timestamp);Balance Helpers
Convenience methods for the most common balance queries:
// Get full balance for an account (HBAR + tokens)
const balance = await client.balances.getForAccount('0.0.98');
console.log(balance.account, balance.balance, balance.tokens);
// Get a specific token balance
const tokenBal = await client.balances.getTokenBalance('0.0.98', '0.0.456');
if (tokenBal) {
console.log(tokenBal.token_id, tokenBal.balance);
}Scheduled Transaction Helpers
High-level workflows for scheduled transactions:
import {
waitForExecution,
getSignatureProgress,
listSchedulesByAccount,
decodeTransactionBody,
} from '@satianurag/hiero-mirror-client';
// Poll until a schedule executes, is deleted, or expires
const result = await waitForExecution(client.schedules, '0.0.1234', {
interval: 2000,
timeout: 120_000,
onPoll: (schedule) => console.log('Polling...', schedule.schedule_id),
});
console.log(result.status); // 'executed' | 'deleted' | 'expired'
// Check signature progress
const progress = await getSignatureProgress(client.schedules, '0.0.1234');
console.log(progress.signatureCount, progress.signedKeys);
// List schedules by creator account, filtered by status
const pending = await listSchedulesByAccount(client.schedules, '0.0.100', {
status: 'pending',
});
// Decode the base64 transaction body
const decoded = decodeTransactionBody(schedule);
console.log(decoded.hex, decoded.byteLength);Utilities
Additional utilities available via the /utils subpath:
import {
base64ToHex, hexToBase64, fromString, toDate,
tinybarToHbar, hbarToTinybar, formatHbar,
} from '@satianurag/hiero-mirror-client/utils';
// HBAR conversion (BigInt-safe, handles up to 8 decimal places)
tinybarToHbar('100000000'); // '1'
tinybarToHbar('150000000'); // '1.5'
tinybarToHbar('1'); // '0.00000001'
hbarToTinybar('2.5'); // '250000000'
formatHbar('100000000'); // '1 HBAR'
// Encoding conversions
const hex = base64ToHex('bZL5Ig=='); // '0x6d92f922'
// Timestamp handling with full nanosecond precision
const ts = fromString('1710000000.123456789');
ts.seconds; // 1710000000n (BigInt)
ts.nanos; // 123456789MCP Tool Descriptors (AI Agent Integration)
Export MCP-compatible tool descriptors for AI agent frameworks:
import { MirrorNodeClient, mirrorNodeTools } from '@satianurag/hiero-mirror-client';
const client = new MirrorNodeClient({ network: 'mainnet' });
// Register with any MCP-compatible server
for (const tool of mirrorNodeTools) {
server.registerTool(tool.name, tool.description, tool.inputSchema, (input) =>
tool.execute(client, input),
);
}
// 16 tools covering: accounts, balances, tokens, transactions,
// schedules, topics, contracts, blocks, and networkTypeScript
All types are exported for full TypeScript support:
import type {
AccountDetail,
TokenSummary,
Transaction,
NetworkStake,
// New helper types
WaitForExecutionOptions,
ExecutionResult,
SignatureProgress,
WaitForTransactionOptions,
ToolDescriptor,
} from '@satianurag/hiero-mirror-client';Int64 Safety
The SDK automatically preserves precision for all large integer values. Fields like balance, stake, and total_supply that exceed Number.MAX_SAFE_INTEGER are returned as strings — never silently truncated.
Contributing
See CONTRIBUTING.md for development setup, coding guidelines, and the pull request process.
This project uses the Developer Certificate of Origin (DCO). All commits must be signed off (git commit -s).
License
MIT — see LICENSE for details.
