@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 +107 -4
- package/dist/hbar.cjs +214 -0
- package/dist/hbar.cjs.map +1 -0
- package/dist/hbar.d.cts +50 -0
- package/dist/hbar.d.cts.map +1 -0
- package/dist/hbar.d.mts +50 -0
- package/dist/hbar.d.mts.map +1 -0
- package/dist/hbar.mjs +173 -0
- package/dist/hbar.mjs.map +1 -0
- package/dist/index.cjs +444 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +157 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +157 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +437 -4
- package/dist/index.mjs.map +1 -1
- package/dist/utils.cjs +8 -98
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +3 -1
- package/dist/utils.d.cts.map +1 -1
- package/dist/utils.d.mts +3 -1
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs +2 -95
- package/dist/utils.mjs.map +1 -1
- package/package.json +1 -1
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** —
|
|
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` |
|
|
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` |
|
|
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 {
|
|
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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"}
|
package/dist/hbar.d.cts
ADDED
|
@@ -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"}
|
package/dist/hbar.d.mts
ADDED
|
@@ -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
|