@satianurag/hiero-mirror-client 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@ Built for precision, type safety, and developer ergonomics — no silent data co
6
6
 
7
7
  ## Features
8
8
 
9
- - **Full API coverage** — 46 methods across 9 resource groups
9
+ - **Full API coverage** — 49 methods across 9 resource groups
10
10
  - **Safe JSON parsing** — int64 values preserved as strings (no precision loss)
11
11
  - **Three pagination patterns** — `await`, `for await...of`, `.pages()`
12
12
  - **Adaptive HCS streaming** — topic message polling with auto-backoff
@@ -14,6 +14,10 @@ Built for precision, type safety, and developer ergonomics — no silent data co
14
14
  - **Typed errors** — structured error hierarchy (`HieroNotFoundError`, `HieroRateLimitError`, etc.)
15
15
  - **Dual output** — ESM + CJS with full `.d.ts` declarations
16
16
  - **Zero platform dependencies** — works in Node.js 22+, Deno, Bun, and browsers
17
+ - **HBAR conversion utilities** — `tinybarToHbar`, `hbarToTinybar`, `formatHbar`
18
+ - **Scheduled transaction helpers** — poll execution, track signatures, filter by account
19
+ - **Transaction confirmation poller** — `waitFor()` bridges consensus-to-mirror-node lag
20
+ - **MCP tool descriptors** — AI agent integration via Model Context Protocol
17
21
 
18
22
  ## Installation
19
23
 
@@ -125,21 +129,94 @@ const client = new MirrorNodeClient({
125
129
  | Resource | Methods | Key Operations |
126
130
  |----------|---------|----------------|
127
131
  | `accounts` | 10 | list, get, NFTs, tokens, rewards, allowances, airdrops |
128
- | `balances` | 1 | list |
132
+ | `balances` | 3 | list, getForAccount, getTokenBalance |
129
133
  | `blocks` | 2 | list, get |
130
134
  | `contracts` | 12 | list, get, call (POST), results, actions, logs, state, opcodes |
131
135
  | `network` | 6 | exchangeRate, fees, estimateFees (POST), nodes, stake, supply |
132
136
  | `schedules` | 2 | list, get |
133
137
  | `tokens` | 6 | list, get, balances, NFTs, NFT transactions |
134
138
  | `topics` | 5 | get, messages, stream |
135
- | `transactions` | 2 | list, get |
139
+ | `transactions` | 3 | list, get, waitFor |
140
+
141
+ ## Transaction Confirmation Poller
142
+
143
+ Bridge the consensus-to-mirror-node ingestion lag (typically 3-7s):
144
+
145
+ ```typescript
146
+ // Poll until the transaction appears on the mirror node
147
+ const tx = await client.transactions.waitFor(
148
+ '0.0.1234@1615422161.673238162',
149
+ { timeout: 15_000 },
150
+ );
151
+ console.log(tx.result, tx.consensus_timestamp);
152
+ ```
153
+
154
+ ## Balance Helpers
155
+
156
+ Convenience methods for the most common balance queries:
157
+
158
+ ```typescript
159
+ // Get full balance for an account (HBAR + tokens)
160
+ const balance = await client.balances.getForAccount('0.0.98');
161
+ console.log(balance.account, balance.balance, balance.tokens);
162
+
163
+ // Get a specific token balance
164
+ const tokenBal = await client.balances.getTokenBalance('0.0.98', '0.0.456');
165
+ if (tokenBal) {
166
+ console.log(tokenBal.token_id, tokenBal.balance);
167
+ }
168
+ ```
169
+
170
+ ## Scheduled Transaction Helpers
171
+
172
+ High-level workflows for scheduled transactions:
173
+
174
+ ```typescript
175
+ import {
176
+ waitForExecution,
177
+ getSignatureProgress,
178
+ listSchedulesByAccount,
179
+ decodeTransactionBody,
180
+ } from '@satianurag/hiero-mirror-client';
181
+
182
+ // Poll until a schedule executes, is deleted, or expires
183
+ const result = await waitForExecution(client.schedules, '0.0.1234', {
184
+ interval: 2000,
185
+ timeout: 120_000,
186
+ onPoll: (schedule) => console.log('Polling...', schedule.schedule_id),
187
+ });
188
+ console.log(result.status); // 'executed' | 'deleted' | 'expired'
189
+
190
+ // Check signature progress
191
+ const progress = await getSignatureProgress(client.schedules, '0.0.1234');
192
+ console.log(progress.signatureCount, progress.signedKeys);
193
+
194
+ // List schedules by creator account, filtered by status
195
+ const pending = await listSchedulesByAccount(client.schedules, '0.0.100', {
196
+ status: 'pending',
197
+ });
198
+
199
+ // Decode the base64 transaction body
200
+ const decoded = decodeTransactionBody(schedule);
201
+ console.log(decoded.hex, decoded.byteLength);
202
+ ```
136
203
 
137
204
  ## Utilities
138
205
 
139
206
  Additional utilities available via the `/utils` subpath:
140
207
 
141
208
  ```typescript
142
- import { base64ToHex, hexToBase64, fromString, toDate } from '@satianurag/hiero-mirror-client/utils';
209
+ import {
210
+ base64ToHex, hexToBase64, fromString, toDate,
211
+ tinybarToHbar, hbarToTinybar, formatHbar,
212
+ } from '@satianurag/hiero-mirror-client/utils';
213
+
214
+ // HBAR conversion (BigInt-safe, handles up to 8 decimal places)
215
+ tinybarToHbar('100000000'); // '1'
216
+ tinybarToHbar('150000000'); // '1.5'
217
+ tinybarToHbar('1'); // '0.00000001'
218
+ hbarToTinybar('2.5'); // '250000000'
219
+ formatHbar('100000000'); // '1 HBAR'
143
220
 
144
221
  // Encoding conversions
145
222
  const hex = base64ToHex('bZL5Ig=='); // '0x6d92f922'
@@ -150,6 +227,26 @@ ts.seconds; // 1710000000n (BigInt)
150
227
  ts.nanos; // 123456789
151
228
  ```
152
229
 
230
+ ## MCP Tool Descriptors (AI Agent Integration)
231
+
232
+ Export MCP-compatible tool descriptors for AI agent frameworks:
233
+
234
+ ```typescript
235
+ import { MirrorNodeClient, mirrorNodeTools } from '@satianurag/hiero-mirror-client';
236
+
237
+ const client = new MirrorNodeClient({ network: 'mainnet' });
238
+
239
+ // Register with any MCP-compatible server
240
+ for (const tool of mirrorNodeTools) {
241
+ server.registerTool(tool.name, tool.description, tool.inputSchema, (input) =>
242
+ tool.execute(client, input),
243
+ );
244
+ }
245
+
246
+ // 16 tools covering: accounts, balances, tokens, transactions,
247
+ // schedules, topics, contracts, blocks, and network
248
+ ```
249
+
153
250
  ## TypeScript
154
251
 
155
252
  All types are exported for full TypeScript support:
@@ -160,6 +257,12 @@ import type {
160
257
  TokenSummary,
161
258
  Transaction,
162
259
  NetworkStake,
260
+ // New helper types
261
+ WaitForExecutionOptions,
262
+ ExecutionResult,
263
+ SignatureProgress,
264
+ WaitForTransactionOptions,
265
+ ToolDescriptor,
163
266
  } from '@satianurag/hiero-mirror-client';
164
267
  ```
165
268
 
package/dist/hbar.cjs ADDED
@@ -0,0 +1,214 @@
1
+ //#region src/utils/encoding.ts
2
+ /**
3
+ * Cross-platform encoding utilities.
4
+ *
5
+ * No dependency on Node.js `Buffer` — uses Web-standard APIs only.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Base64-to-hex conversion.
11
+ *
12
+ * ```ts
13
+ * base64ToHex('SGVsbG8=') // → '48656c6c6f'
14
+ * ```
15
+ */
16
+ function base64ToHex(base64) {
17
+ const bytes = base64ToBytes(base64);
18
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
19
+ }
20
+ /**
21
+ * Hex-to-base64 conversion.
22
+ *
23
+ * Accepts optional 0x prefix.
24
+ *
25
+ * ```ts
26
+ * hexToBase64('48656c6c6f') // → 'SGVsbG8='
27
+ * hexToBase64('0x48656c6c6f') // → 'SGVsbG8='
28
+ * ```
29
+ */
30
+ function hexToBase64(hex) {
31
+ const cleaned = hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
32
+ if (cleaned.length % 2 !== 0) throw new Error(`Invalid hex string: odd length (${cleaned.length})`);
33
+ const bytes = new Uint8Array(cleaned.length / 2);
34
+ for (let i = 0; i < cleaned.length; i += 2) bytes[i / 2] = Number.parseInt(cleaned.slice(i, i + 2), 16);
35
+ return bytesToBase64(bytes);
36
+ }
37
+ /**
38
+ * Convert a Uint8Array to a hex string (lowercase, no prefix).
39
+ */
40
+ function bytesToHex(bytes) {
41
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
42
+ }
43
+ /**
44
+ * Convert a hex string (with optional 0x prefix) to Uint8Array.
45
+ */
46
+ function hexToBytes(hex) {
47
+ const cleaned = hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
48
+ if (cleaned.length % 2 !== 0) throw new Error(`Invalid hex string: odd length (${cleaned.length})`);
49
+ const bytes = new Uint8Array(cleaned.length / 2);
50
+ for (let i = 0; i < cleaned.length; i += 2) bytes[i / 2] = Number.parseInt(cleaned.slice(i, i + 2), 16);
51
+ return bytes;
52
+ }
53
+ const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54
+ function base64ToBytes(base64) {
55
+ if (typeof atob === "function") {
56
+ const binary = atob(base64);
57
+ const bytes = new Uint8Array(binary.length);
58
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
59
+ return bytes;
60
+ }
61
+ const cleaned = base64.replace(/=+$/, "");
62
+ const byteLength = cleaned.length * 3 >> 2;
63
+ const bytes = new Uint8Array(byteLength);
64
+ let byteIndex = 0;
65
+ for (let i = 0; i < cleaned.length; i += 4) {
66
+ const a = BASE64_CHARS.indexOf(cleaned.charAt(i));
67
+ const b = BASE64_CHARS.indexOf(cleaned.charAt(i + 1));
68
+ const c = BASE64_CHARS.indexOf(i + 2 < cleaned.length ? cleaned.charAt(i + 2) : "A");
69
+ const d = BASE64_CHARS.indexOf(i + 3 < cleaned.length ? cleaned.charAt(i + 3) : "A");
70
+ bytes[byteIndex++] = a << 2 | b >> 4;
71
+ if (i + 2 < cleaned.length) bytes[byteIndex++] = (b & 15) << 4 | c >> 2;
72
+ if (i + 3 < cleaned.length) bytes[byteIndex++] = (c & 3) << 6 | d;
73
+ }
74
+ return bytes;
75
+ }
76
+ function bytesToBase64(bytes) {
77
+ if (typeof btoa === "function") {
78
+ let binary = "";
79
+ for (const b of bytes) binary += String.fromCharCode(b);
80
+ return btoa(binary);
81
+ }
82
+ let result = "";
83
+ for (let i = 0; i < bytes.length; i += 3) {
84
+ const a = bytes[i];
85
+ const b = bytes[i + 1] ?? 0;
86
+ const c = bytes[i + 2] ?? 0;
87
+ result += BASE64_CHARS[a >> 2 & 63];
88
+ result += BASE64_CHARS[(a & 3) << 4 | b >> 4 & 15];
89
+ result += i + 1 < bytes.length ? BASE64_CHARS[(b & 15) << 2 | c >> 6 & 3] : "=";
90
+ result += i + 2 < bytes.length ? BASE64_CHARS[c & 63] : "=";
91
+ }
92
+ return result;
93
+ }
94
+ //#endregion
95
+ //#region src/utils/hbar.ts
96
+ /**
97
+ * HBAR / tinybar conversion utilities.
98
+ *
99
+ * The Hedera mirror node returns balances in **tinybars** (as strings for int64 safety).
100
+ * 1 HBAR = 100,000,000 tinybars.
101
+ *
102
+ * These helpers eliminate the most common source of confusion when working
103
+ * with Hedera balances.
104
+ *
105
+ * @packageDocumentation
106
+ */
107
+ /** 1 HBAR = 100,000,000 tinybars. */
108
+ const TINYBARS_PER_HBAR = 100000000n;
109
+ /**
110
+ * Converts tinybars (string) to HBAR (string) with up to 8 decimal places.
111
+ *
112
+ * ```ts
113
+ * tinybarToHbar('100000000') // '1'
114
+ * tinybarToHbar('150000000') // '1.5'
115
+ * tinybarToHbar('1') // '0.00000001'
116
+ * tinybarToHbar('-250000000') // '-2.5'
117
+ * ```
118
+ */
119
+ function tinybarToHbar(tinybars) {
120
+ const value = BigInt(tinybars);
121
+ const negative = value < 0n;
122
+ const abs = negative ? -value : value;
123
+ const whole = abs / TINYBARS_PER_HBAR;
124
+ const remainder = abs % TINYBARS_PER_HBAR;
125
+ if (remainder === 0n) return `${negative ? "-" : ""}${whole}`;
126
+ const fracStr = remainder.toString().padStart(8, "0").replace(/0+$/, "");
127
+ return `${negative ? "-" : ""}${whole}.${fracStr}`;
128
+ }
129
+ /**
130
+ * Converts HBAR (string or number) to tinybars (string).
131
+ *
132
+ * Accepts up to 8 decimal places.
133
+ *
134
+ * ```ts
135
+ * hbarToTinybar('1') // '100000000'
136
+ * hbarToTinybar('1.5') // '150000000'
137
+ * hbarToTinybar('0.00000001') // '1'
138
+ * hbarToTinybar('-2.5') // '-250000000'
139
+ * ```
140
+ */
141
+ function hbarToTinybar(hbar) {
142
+ const str = String(hbar);
143
+ const negative = str.startsWith("-");
144
+ const abs = negative ? str.slice(1) : str;
145
+ const dotIndex = abs.indexOf(".");
146
+ if (dotIndex === -1) {
147
+ const result = BigInt(abs) * TINYBARS_PER_HBAR;
148
+ return `${negative ? "-" : ""}${result}`;
149
+ }
150
+ const wholePart = abs.slice(0, dotIndex) || "0";
151
+ const fracPart = abs.slice(dotIndex + 1);
152
+ if (fracPart.length > 8) throw new RangeError(`HBAR value "${str}" exceeds maximum precision of 8 decimal places (tinybars)`);
153
+ const paddedFrac = fracPart.padEnd(8, "0");
154
+ const total = BigInt(wholePart) * TINYBARS_PER_HBAR + BigInt(paddedFrac);
155
+ return `${negative ? "-" : ""}${total}`;
156
+ }
157
+ /**
158
+ * Formats a tinybar amount as a human-readable HBAR string with the symbol.
159
+ *
160
+ * ```ts
161
+ * formatHbar('100000000') // '1 HBAR'
162
+ * formatHbar('150000000') // '1.5 HBAR'
163
+ * formatHbar('1') // '0.00000001 HBAR'
164
+ * formatHbar('-250000000') // '-2.5 HBAR'
165
+ * ```
166
+ */
167
+ function formatHbar(tinybars) {
168
+ return `${tinybarToHbar(tinybars)} HBAR`;
169
+ }
170
+ //#endregion
171
+ Object.defineProperty(exports, "base64ToHex", {
172
+ enumerable: true,
173
+ get: function() {
174
+ return base64ToHex;
175
+ }
176
+ });
177
+ Object.defineProperty(exports, "bytesToHex", {
178
+ enumerable: true,
179
+ get: function() {
180
+ return bytesToHex;
181
+ }
182
+ });
183
+ Object.defineProperty(exports, "formatHbar", {
184
+ enumerable: true,
185
+ get: function() {
186
+ return formatHbar;
187
+ }
188
+ });
189
+ Object.defineProperty(exports, "hbarToTinybar", {
190
+ enumerable: true,
191
+ get: function() {
192
+ return hbarToTinybar;
193
+ }
194
+ });
195
+ Object.defineProperty(exports, "hexToBase64", {
196
+ enumerable: true,
197
+ get: function() {
198
+ return hexToBase64;
199
+ }
200
+ });
201
+ Object.defineProperty(exports, "hexToBytes", {
202
+ enumerable: true,
203
+ get: function() {
204
+ return hexToBytes;
205
+ }
206
+ });
207
+ Object.defineProperty(exports, "tinybarToHbar", {
208
+ enumerable: true,
209
+ get: function() {
210
+ return tinybarToHbar;
211
+ }
212
+ });
213
+
214
+ //# sourceMappingURL=hbar.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hbar.cjs","names":[],"sources":["../src/utils/encoding.ts","../src/utils/hbar.ts"],"sourcesContent":["/**\n * Cross-platform encoding utilities.\n *\n * No dependency on Node.js `Buffer` — uses Web-standard APIs only.\n *\n * @packageDocumentation\n */\n\n/**\n * Base64-to-hex conversion.\n *\n * ```ts\n * base64ToHex('SGVsbG8=') // → '48656c6c6f'\n * ```\n */\nexport function base64ToHex(base64: string): string {\n const bytes = base64ToBytes(base64);\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Hex-to-base64 conversion.\n *\n * Accepts optional 0x prefix.\n *\n * ```ts\n * hexToBase64('48656c6c6f') // → 'SGVsbG8='\n * hexToBase64('0x48656c6c6f') // → 'SGVsbG8='\n * ```\n */\nexport function hexToBase64(hex: string): string {\n const cleaned = hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex;\n if (cleaned.length % 2 !== 0) {\n throw new Error(`Invalid hex string: odd length (${cleaned.length})`);\n }\n const bytes = new Uint8Array(cleaned.length / 2);\n for (let i = 0; i < cleaned.length; i += 2) {\n bytes[i / 2] = Number.parseInt(cleaned.slice(i, i + 2), 16);\n }\n return bytesToBase64(bytes);\n}\n\n/**\n * Convert a Uint8Array to a hex string (lowercase, no prefix).\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Convert a hex string (with optional 0x prefix) to Uint8Array.\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const cleaned = hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex;\n if (cleaned.length % 2 !== 0) {\n throw new Error(`Invalid hex string: odd length (${cleaned.length})`);\n }\n const bytes = new Uint8Array(cleaned.length / 2);\n for (let i = 0; i < cleaned.length; i += 2) {\n bytes[i / 2] = Number.parseInt(cleaned.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers (no Buffer, cross-platform)\n// ---------------------------------------------------------------------------\n\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\nfunction base64ToBytes(base64: string): Uint8Array {\n // Use atob if available (browser + Node 16+)\n if (typeof atob === 'function') {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n }\n\n // Manual fallback (should rarely be needed)\n const cleaned = base64.replace(/=+$/, '');\n const byteLength = (cleaned.length * 3) >> 2;\n const bytes = new Uint8Array(byteLength);\n let byteIndex = 0;\n\n for (let i = 0; i < cleaned.length; i += 4) {\n const a = BASE64_CHARS.indexOf(cleaned.charAt(i));\n const b = BASE64_CHARS.indexOf(cleaned.charAt(i + 1));\n const c = BASE64_CHARS.indexOf(i + 2 < cleaned.length ? cleaned.charAt(i + 2) : 'A');\n const d = BASE64_CHARS.indexOf(i + 3 < cleaned.length ? cleaned.charAt(i + 3) : 'A');\n\n bytes[byteIndex++] = (a << 2) | (b >> 4);\n if (i + 2 < cleaned.length) bytes[byteIndex++] = ((b & 15) << 4) | (c >> 2);\n if (i + 3 < cleaned.length) bytes[byteIndex++] = ((c & 3) << 6) | d;\n }\n\n return bytes;\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Use btoa if available (browser + Node 16+)\n if (typeof btoa === 'function') {\n let binary = '';\n for (const b of bytes) {\n binary += String.fromCharCode(b);\n }\n return btoa(binary);\n }\n\n // Manual fallback\n let result = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const a = bytes[i]!;\n const b = bytes[i + 1] ?? 0;\n const c = bytes[i + 2] ?? 0;\n\n result += BASE64_CHARS[(a >> 2) & 63];\n result += BASE64_CHARS[((a & 3) << 4) | ((b >> 4) & 15)];\n result += i + 1 < bytes.length ? BASE64_CHARS[((b & 15) << 2) | ((c >> 6) & 3)] : '=';\n result += i + 2 < bytes.length ? BASE64_CHARS[c & 63] : '=';\n }\n\n return result;\n}\n","/**\n * HBAR / tinybar conversion utilities.\n *\n * The Hedera mirror node returns balances in **tinybars** (as strings for int64 safety).\n * 1 HBAR = 100,000,000 tinybars.\n *\n * These helpers eliminate the most common source of confusion when working\n * with Hedera balances.\n *\n * @packageDocumentation\n */\n\n/** 1 HBAR = 100,000,000 tinybars. */\nconst TINYBARS_PER_HBAR = 100_000_000n;\n\n/**\n * Converts tinybars (string) to HBAR (string) with up to 8 decimal places.\n *\n * ```ts\n * tinybarToHbar('100000000') // '1'\n * tinybarToHbar('150000000') // '1.5'\n * tinybarToHbar('1') // '0.00000001'\n * tinybarToHbar('-250000000') // '-2.5'\n * ```\n */\nexport function tinybarToHbar(tinybars: string): string {\n const value = BigInt(tinybars);\n const negative = value < 0n;\n const abs = negative ? -value : value;\n\n const whole = abs / TINYBARS_PER_HBAR;\n const remainder = abs % TINYBARS_PER_HBAR;\n\n if (remainder === 0n) {\n return `${negative ? '-' : ''}${whole}`;\n }\n\n // Pad remainder to 8 digits then strip trailing zeros\n const fracStr = remainder.toString().padStart(8, '0').replace(/0+$/, '');\n return `${negative ? '-' : ''}${whole}.${fracStr}`;\n}\n\n/**\n * Converts HBAR (string or number) to tinybars (string).\n *\n * Accepts up to 8 decimal places.\n *\n * ```ts\n * hbarToTinybar('1') // '100000000'\n * hbarToTinybar('1.5') // '150000000'\n * hbarToTinybar('0.00000001') // '1'\n * hbarToTinybar('-2.5') // '-250000000'\n * ```\n */\nexport function hbarToTinybar(hbar: string | number): string {\n const str = String(hbar);\n const negative = str.startsWith('-');\n const abs = negative ? str.slice(1) : str;\n\n const dotIndex = abs.indexOf('.');\n if (dotIndex === -1) {\n // Whole number\n const result = BigInt(abs) * TINYBARS_PER_HBAR;\n return `${negative ? '-' : ''}${result}`;\n }\n\n const wholePart = abs.slice(0, dotIndex) || '0';\n const fracPart = abs.slice(dotIndex + 1);\n\n if (fracPart.length > 8) {\n throw new RangeError(\n `HBAR value \"${str}\" exceeds maximum precision of 8 decimal places (tinybars)`,\n );\n }\n\n // Pad fractional part to exactly 8 digits\n const paddedFrac = fracPart.padEnd(8, '0');\n const wholeTinybars = BigInt(wholePart) * TINYBARS_PER_HBAR;\n const fracTinybars = BigInt(paddedFrac);\n const total = wholeTinybars + fracTinybars;\n\n return `${negative ? '-' : ''}${total}`;\n}\n\n/**\n * Formats a tinybar amount as a human-readable HBAR string with the symbol.\n *\n * ```ts\n * formatHbar('100000000') // '1 HBAR'\n * formatHbar('150000000') // '1.5 HBAR'\n * formatHbar('1') // '0.00000001 HBAR'\n * formatHbar('-250000000') // '-2.5 HBAR'\n * ```\n */\nexport function formatHbar(tinybars: string): string {\n return `${tinybarToHbar(tinybars)} HBAR`;\n}\n"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ //#region src/utils/hbar.d.ts
2
+ /**
3
+ * HBAR / tinybar conversion utilities.
4
+ *
5
+ * The Hedera mirror node returns balances in **tinybars** (as strings for int64 safety).
6
+ * 1 HBAR = 100,000,000 tinybars.
7
+ *
8
+ * These helpers eliminate the most common source of confusion when working
9
+ * with Hedera balances.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+ /**
14
+ * Converts tinybars (string) to HBAR (string) with up to 8 decimal places.
15
+ *
16
+ * ```ts
17
+ * tinybarToHbar('100000000') // '1'
18
+ * tinybarToHbar('150000000') // '1.5'
19
+ * tinybarToHbar('1') // '0.00000001'
20
+ * tinybarToHbar('-250000000') // '-2.5'
21
+ * ```
22
+ */
23
+ declare function tinybarToHbar(tinybars: string): string;
24
+ /**
25
+ * Converts HBAR (string or number) to tinybars (string).
26
+ *
27
+ * Accepts up to 8 decimal places.
28
+ *
29
+ * ```ts
30
+ * hbarToTinybar('1') // '100000000'
31
+ * hbarToTinybar('1.5') // '150000000'
32
+ * hbarToTinybar('0.00000001') // '1'
33
+ * hbarToTinybar('-2.5') // '-250000000'
34
+ * ```
35
+ */
36
+ declare function hbarToTinybar(hbar: string | number): string;
37
+ /**
38
+ * Formats a tinybar amount as a human-readable HBAR string with the symbol.
39
+ *
40
+ * ```ts
41
+ * formatHbar('100000000') // '1 HBAR'
42
+ * formatHbar('150000000') // '1.5 HBAR'
43
+ * formatHbar('1') // '0.00000001 HBAR'
44
+ * formatHbar('-250000000') // '-2.5 HBAR'
45
+ * ```
46
+ */
47
+ declare function formatHbar(tinybars: string): string;
48
+ //#endregion
49
+ export { hbarToTinybar as n, tinybarToHbar as r, formatHbar as t };
50
+ //# sourceMappingURL=hbar.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hbar.d.cts","names":[],"sources":["../src/utils/hbar.ts"],"mappings":";;AAyBA;;;;;AA6BA;;;;;AAwCA;;;;;;;;;;AAAA,iBArEgB,aAAA,CAAc,QAAA;;;;;;;;;;;;;iBA6Bd,aAAA,CAAc,IAAA;;;;;;;;;;;iBAwCd,UAAA,CAAW,QAAA"}
@@ -0,0 +1,50 @@
1
+ //#region src/utils/hbar.d.ts
2
+ /**
3
+ * HBAR / tinybar conversion utilities.
4
+ *
5
+ * The Hedera mirror node returns balances in **tinybars** (as strings for int64 safety).
6
+ * 1 HBAR = 100,000,000 tinybars.
7
+ *
8
+ * These helpers eliminate the most common source of confusion when working
9
+ * with Hedera balances.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+ /**
14
+ * Converts tinybars (string) to HBAR (string) with up to 8 decimal places.
15
+ *
16
+ * ```ts
17
+ * tinybarToHbar('100000000') // '1'
18
+ * tinybarToHbar('150000000') // '1.5'
19
+ * tinybarToHbar('1') // '0.00000001'
20
+ * tinybarToHbar('-250000000') // '-2.5'
21
+ * ```
22
+ */
23
+ declare function tinybarToHbar(tinybars: string): string;
24
+ /**
25
+ * Converts HBAR (string or number) to tinybars (string).
26
+ *
27
+ * Accepts up to 8 decimal places.
28
+ *
29
+ * ```ts
30
+ * hbarToTinybar('1') // '100000000'
31
+ * hbarToTinybar('1.5') // '150000000'
32
+ * hbarToTinybar('0.00000001') // '1'
33
+ * hbarToTinybar('-2.5') // '-250000000'
34
+ * ```
35
+ */
36
+ declare function hbarToTinybar(hbar: string | number): string;
37
+ /**
38
+ * Formats a tinybar amount as a human-readable HBAR string with the symbol.
39
+ *
40
+ * ```ts
41
+ * formatHbar('100000000') // '1 HBAR'
42
+ * formatHbar('150000000') // '1.5 HBAR'
43
+ * formatHbar('1') // '0.00000001 HBAR'
44
+ * formatHbar('-250000000') // '-2.5 HBAR'
45
+ * ```
46
+ */
47
+ declare function formatHbar(tinybars: string): string;
48
+ //#endregion
49
+ export { hbarToTinybar as n, tinybarToHbar as r, formatHbar as t };
50
+ //# sourceMappingURL=hbar.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hbar.d.mts","names":[],"sources":["../src/utils/hbar.ts"],"mappings":";;AAyBA;;;;;AA6BA;;;;;AAwCA;;;;;;;;;;AAAA,iBArEgB,aAAA,CAAc,QAAA;;;;;;;;;;;;;iBA6Bd,aAAA,CAAc,IAAA;;;;;;;;;;;iBAwCd,UAAA,CAAW,QAAA"}
package/dist/hbar.mjs ADDED
@@ -0,0 +1,173 @@
1
+ //#region src/utils/encoding.ts
2
+ /**
3
+ * Cross-platform encoding utilities.
4
+ *
5
+ * No dependency on Node.js `Buffer` — uses Web-standard APIs only.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Base64-to-hex conversion.
11
+ *
12
+ * ```ts
13
+ * base64ToHex('SGVsbG8=') // → '48656c6c6f'
14
+ * ```
15
+ */
16
+ function base64ToHex(base64) {
17
+ const bytes = base64ToBytes(base64);
18
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
19
+ }
20
+ /**
21
+ * Hex-to-base64 conversion.
22
+ *
23
+ * Accepts optional 0x prefix.
24
+ *
25
+ * ```ts
26
+ * hexToBase64('48656c6c6f') // → 'SGVsbG8='
27
+ * hexToBase64('0x48656c6c6f') // → 'SGVsbG8='
28
+ * ```
29
+ */
30
+ function hexToBase64(hex) {
31
+ const cleaned = hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
32
+ if (cleaned.length % 2 !== 0) throw new Error(`Invalid hex string: odd length (${cleaned.length})`);
33
+ const bytes = new Uint8Array(cleaned.length / 2);
34
+ for (let i = 0; i < cleaned.length; i += 2) bytes[i / 2] = Number.parseInt(cleaned.slice(i, i + 2), 16);
35
+ return bytesToBase64(bytes);
36
+ }
37
+ /**
38
+ * Convert a Uint8Array to a hex string (lowercase, no prefix).
39
+ */
40
+ function bytesToHex(bytes) {
41
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
42
+ }
43
+ /**
44
+ * Convert a hex string (with optional 0x prefix) to Uint8Array.
45
+ */
46
+ function hexToBytes(hex) {
47
+ const cleaned = hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
48
+ if (cleaned.length % 2 !== 0) throw new Error(`Invalid hex string: odd length (${cleaned.length})`);
49
+ const bytes = new Uint8Array(cleaned.length / 2);
50
+ for (let i = 0; i < cleaned.length; i += 2) bytes[i / 2] = Number.parseInt(cleaned.slice(i, i + 2), 16);
51
+ return bytes;
52
+ }
53
+ const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54
+ function base64ToBytes(base64) {
55
+ if (typeof atob === "function") {
56
+ const binary = atob(base64);
57
+ const bytes = new Uint8Array(binary.length);
58
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
59
+ return bytes;
60
+ }
61
+ const cleaned = base64.replace(/=+$/, "");
62
+ const byteLength = cleaned.length * 3 >> 2;
63
+ const bytes = new Uint8Array(byteLength);
64
+ let byteIndex = 0;
65
+ for (let i = 0; i < cleaned.length; i += 4) {
66
+ const a = BASE64_CHARS.indexOf(cleaned.charAt(i));
67
+ const b = BASE64_CHARS.indexOf(cleaned.charAt(i + 1));
68
+ const c = BASE64_CHARS.indexOf(i + 2 < cleaned.length ? cleaned.charAt(i + 2) : "A");
69
+ const d = BASE64_CHARS.indexOf(i + 3 < cleaned.length ? cleaned.charAt(i + 3) : "A");
70
+ bytes[byteIndex++] = a << 2 | b >> 4;
71
+ if (i + 2 < cleaned.length) bytes[byteIndex++] = (b & 15) << 4 | c >> 2;
72
+ if (i + 3 < cleaned.length) bytes[byteIndex++] = (c & 3) << 6 | d;
73
+ }
74
+ return bytes;
75
+ }
76
+ function bytesToBase64(bytes) {
77
+ if (typeof btoa === "function") {
78
+ let binary = "";
79
+ for (const b of bytes) binary += String.fromCharCode(b);
80
+ return btoa(binary);
81
+ }
82
+ let result = "";
83
+ for (let i = 0; i < bytes.length; i += 3) {
84
+ const a = bytes[i];
85
+ const b = bytes[i + 1] ?? 0;
86
+ const c = bytes[i + 2] ?? 0;
87
+ result += BASE64_CHARS[a >> 2 & 63];
88
+ result += BASE64_CHARS[(a & 3) << 4 | b >> 4 & 15];
89
+ result += i + 1 < bytes.length ? BASE64_CHARS[(b & 15) << 2 | c >> 6 & 3] : "=";
90
+ result += i + 2 < bytes.length ? BASE64_CHARS[c & 63] : "=";
91
+ }
92
+ return result;
93
+ }
94
+ //#endregion
95
+ //#region src/utils/hbar.ts
96
+ /**
97
+ * HBAR / tinybar conversion utilities.
98
+ *
99
+ * The Hedera mirror node returns balances in **tinybars** (as strings for int64 safety).
100
+ * 1 HBAR = 100,000,000 tinybars.
101
+ *
102
+ * These helpers eliminate the most common source of confusion when working
103
+ * with Hedera balances.
104
+ *
105
+ * @packageDocumentation
106
+ */
107
+ /** 1 HBAR = 100,000,000 tinybars. */
108
+ const TINYBARS_PER_HBAR = 100000000n;
109
+ /**
110
+ * Converts tinybars (string) to HBAR (string) with up to 8 decimal places.
111
+ *
112
+ * ```ts
113
+ * tinybarToHbar('100000000') // '1'
114
+ * tinybarToHbar('150000000') // '1.5'
115
+ * tinybarToHbar('1') // '0.00000001'
116
+ * tinybarToHbar('-250000000') // '-2.5'
117
+ * ```
118
+ */
119
+ function tinybarToHbar(tinybars) {
120
+ const value = BigInt(tinybars);
121
+ const negative = value < 0n;
122
+ const abs = negative ? -value : value;
123
+ const whole = abs / TINYBARS_PER_HBAR;
124
+ const remainder = abs % TINYBARS_PER_HBAR;
125
+ if (remainder === 0n) return `${negative ? "-" : ""}${whole}`;
126
+ const fracStr = remainder.toString().padStart(8, "0").replace(/0+$/, "");
127
+ return `${negative ? "-" : ""}${whole}.${fracStr}`;
128
+ }
129
+ /**
130
+ * Converts HBAR (string or number) to tinybars (string).
131
+ *
132
+ * Accepts up to 8 decimal places.
133
+ *
134
+ * ```ts
135
+ * hbarToTinybar('1') // '100000000'
136
+ * hbarToTinybar('1.5') // '150000000'
137
+ * hbarToTinybar('0.00000001') // '1'
138
+ * hbarToTinybar('-2.5') // '-250000000'
139
+ * ```
140
+ */
141
+ function hbarToTinybar(hbar) {
142
+ const str = String(hbar);
143
+ const negative = str.startsWith("-");
144
+ const abs = negative ? str.slice(1) : str;
145
+ const dotIndex = abs.indexOf(".");
146
+ if (dotIndex === -1) {
147
+ const result = BigInt(abs) * TINYBARS_PER_HBAR;
148
+ return `${negative ? "-" : ""}${result}`;
149
+ }
150
+ const wholePart = abs.slice(0, dotIndex) || "0";
151
+ const fracPart = abs.slice(dotIndex + 1);
152
+ if (fracPart.length > 8) throw new RangeError(`HBAR value "${str}" exceeds maximum precision of 8 decimal places (tinybars)`);
153
+ const paddedFrac = fracPart.padEnd(8, "0");
154
+ const total = BigInt(wholePart) * TINYBARS_PER_HBAR + BigInt(paddedFrac);
155
+ return `${negative ? "-" : ""}${total}`;
156
+ }
157
+ /**
158
+ * Formats a tinybar amount as a human-readable HBAR string with the symbol.
159
+ *
160
+ * ```ts
161
+ * formatHbar('100000000') // '1 HBAR'
162
+ * formatHbar('150000000') // '1.5 HBAR'
163
+ * formatHbar('1') // '0.00000001 HBAR'
164
+ * formatHbar('-250000000') // '-2.5 HBAR'
165
+ * ```
166
+ */
167
+ function formatHbar(tinybars) {
168
+ return `${tinybarToHbar(tinybars)} HBAR`;
169
+ }
170
+ //#endregion
171
+ export { bytesToHex as a, base64ToHex as i, hbarToTinybar as n, hexToBase64 as o, tinybarToHbar as r, hexToBytes as s, formatHbar as t };
172
+
173
+ //# sourceMappingURL=hbar.mjs.map