@jellylegsai/aether-cli 1.8.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/LICENSE +21 -0
- package/README.md +110 -0
- package/aether-cli-1.0.0.tgz +0 -0
- package/aether-cli-1.8.0.tgz +0 -0
- package/aether-hub-1.0.5.tgz +0 -0
- package/aether-hub-1.1.8.tgz +0 -0
- package/aether-hub-1.2.1.tgz +0 -0
- package/commands/account.js +280 -0
- package/commands/apy.js +499 -0
- package/commands/balance.js +241 -0
- package/commands/blockhash.js +181 -0
- package/commands/broadcast.js +387 -0
- package/commands/claim.js +490 -0
- package/commands/config.js +851 -0
- package/commands/delegations.js +582 -0
- package/commands/doctor.js +769 -0
- package/commands/emergency.js +667 -0
- package/commands/epoch.js +275 -0
- package/commands/fees.js +276 -0
- package/commands/index.js +78 -0
- package/commands/info.js +495 -0
- package/commands/init.js +816 -0
- package/commands/install.js +666 -0
- package/commands/kyc.js +272 -0
- package/commands/logs.js +315 -0
- package/commands/monitor.js +431 -0
- package/commands/multisig.js +701 -0
- package/commands/network.js +429 -0
- package/commands/nft.js +857 -0
- package/commands/ping.js +266 -0
- package/commands/price.js +253 -0
- package/commands/rewards.js +931 -0
- package/commands/sdk-test.js +477 -0
- package/commands/sdk.js +656 -0
- package/commands/slot.js +155 -0
- package/commands/snapshot.js +470 -0
- package/commands/stake-info.js +139 -0
- package/commands/stake-positions.js +205 -0
- package/commands/stake.js +516 -0
- package/commands/stats.js +396 -0
- package/commands/status.js +327 -0
- package/commands/supply.js +391 -0
- package/commands/tps.js +238 -0
- package/commands/transfer.js +495 -0
- package/commands/tx-history.js +346 -0
- package/commands/unstake.js +597 -0
- package/commands/validator-info.js +657 -0
- package/commands/validator-register.js +593 -0
- package/commands/validator-start.js +323 -0
- package/commands/validator-status.js +227 -0
- package/commands/validators.js +626 -0
- package/commands/wallet.js +1570 -0
- package/index.js +593 -0
- package/lib/errors.js +398 -0
- package/package.json +76 -0
- package/sdk/README.md +210 -0
- package/sdk/index.js +1639 -0
- package/sdk/package.json +34 -0
- package/sdk/rpc.js +254 -0
- package/sdk/test.js +85 -0
- package/test/doctor.test.js +76 -0
- package/validator-identity.json +4 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* aether-cli balance
|
|
4
|
+
*
|
|
5
|
+
* Query account balance from the Aether blockchain — real HTTP RPC calls,
|
|
6
|
+
* real data. No stubs, no mocks.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* aether balance <address> Query balance for an address
|
|
10
|
+
* aether balance Query default wallet balance
|
|
11
|
+
* aether balance --json JSON output for scripting
|
|
12
|
+
* aether balance --rpc <url> Query a specific RPC endpoint
|
|
13
|
+
* aether balance --lamports Show balance in lamports (not AETH)
|
|
14
|
+
*
|
|
15
|
+
* Requires AETHER_RPC env var (default: http://127.0.0.1:8899)
|
|
16
|
+
* SDK: @jellylegsai/aether-sdk — makes REAL HTTP RPC calls to the chain
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
|
|
23
|
+
// Import error handling utilities
|
|
24
|
+
const {
|
|
25
|
+
withErrorHandling,
|
|
26
|
+
displayError,
|
|
27
|
+
validateAddress,
|
|
28
|
+
createRpcCaller,
|
|
29
|
+
C
|
|
30
|
+
} = require('../lib/errors');
|
|
31
|
+
|
|
32
|
+
// Import SDK — REAL blockchain RPC calls to http://127.0.0.1:8899
|
|
33
|
+
const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
|
|
34
|
+
const aether = require(sdkPath);
|
|
35
|
+
|
|
36
|
+
const CLI_VERSION = '1.5.0';
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Helpers
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
function getDefaultRpc() {
|
|
43
|
+
return process.env.AETHER_RPC || (aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function createClient(rpc) {
|
|
47
|
+
return new aether.AetherClient({ rpcUrl: rpc });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function lamportsToAeth(lamports) {
|
|
51
|
+
return (Number(lamports) / 1e9).toFixed(6);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function loadConfig() {
|
|
55
|
+
const aetherDir = path.join(os.homedir(), '.aether');
|
|
56
|
+
const cfgPath = path.join(aetherDir, 'config.json');
|
|
57
|
+
if (!fs.existsSync(cfgPath)) return { defaultWallet: null };
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return { defaultWallet: null };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function shortenAddress(addr) {
|
|
67
|
+
if (!addr) return 'unknown';
|
|
68
|
+
if (addr.length <= 10) return addr;
|
|
69
|
+
return `${addr.substring(0, 6)}...${addr.substring(addr.length - 4)}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Argument parsing
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
function parseArgs() {
|
|
77
|
+
const args = process.argv.slice(2);
|
|
78
|
+
const options = {
|
|
79
|
+
rpc: getDefaultRpc(),
|
|
80
|
+
address: null,
|
|
81
|
+
asJson: false,
|
|
82
|
+
showLamports: false,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < args.length; i++) {
|
|
86
|
+
const arg = args[i];
|
|
87
|
+
if (arg === '--rpc' || arg === '-r') {
|
|
88
|
+
options.rpc = args[++i];
|
|
89
|
+
} else if (arg === '--json' || arg === '-j') {
|
|
90
|
+
options.asJson = true;
|
|
91
|
+
} else if (arg === '--lamports' || arg === '-l') {
|
|
92
|
+
options.showLamports = true;
|
|
93
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
94
|
+
showHelp();
|
|
95
|
+
process.exit(0);
|
|
96
|
+
} else if (!arg.startsWith('-') && !options.address) {
|
|
97
|
+
options.address = arg;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return options;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function showHelp() {
|
|
105
|
+
console.log(`
|
|
106
|
+
${C.bright}${C.cyan}aether-cli balance${C.reset} - Query account balance
|
|
107
|
+
|
|
108
|
+
${C.bright}Usage:${C.reset}
|
|
109
|
+
aether balance [address] [options]
|
|
110
|
+
|
|
111
|
+
${C.bright}Options:${C.reset}
|
|
112
|
+
-r, --rpc <url> RPC endpoint (default: ${getDefaultRpc()})
|
|
113
|
+
-j, --json Output as JSON
|
|
114
|
+
-l, --lamports Show balance in lamports instead of AETH
|
|
115
|
+
-h, --help Show this help message
|
|
116
|
+
|
|
117
|
+
${C.bright}Examples:${C.reset}
|
|
118
|
+
aether balance 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZvJkVG6PkD
|
|
119
|
+
aether balance --json
|
|
120
|
+
aether balance --lamports 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZvJkVG6PkD
|
|
121
|
+
`.trim());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Balance fetching with retry logic
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
async function fetchBalance(client, address, options) {
|
|
129
|
+
const { asJson, showLamports } = options;
|
|
130
|
+
|
|
131
|
+
// Validate address format
|
|
132
|
+
try {
|
|
133
|
+
validateAddress(address);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
error.isValidationError = true;
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Create RPC caller with retry logic
|
|
140
|
+
const getBalance = createRpcCaller(client.getBalance.bind(client), {
|
|
141
|
+
maxRetries: 3,
|
|
142
|
+
initialDelay: 500,
|
|
143
|
+
onRetry: (error, attempt, max) => {
|
|
144
|
+
if (!asJson) {
|
|
145
|
+
console.log(
|
|
146
|
+
`${C.yellow}⚠ Retrying balance fetch (attempt ${attempt}/${max})...${C.reset}`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Fetch balance with retry
|
|
153
|
+
const lamports = await getBalance(address);
|
|
154
|
+
|
|
155
|
+
// Handle response
|
|
156
|
+
if (lamports === undefined || lamports === null) {
|
|
157
|
+
throw new Error('Received invalid response from RPC');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const balanceBigInt = BigInt(lamports);
|
|
161
|
+
const aeth = lamportsToAeth(balanceBigInt);
|
|
162
|
+
|
|
163
|
+
if (asJson) {
|
|
164
|
+
console.log(JSON.stringify({
|
|
165
|
+
address,
|
|
166
|
+
lamports: balanceBigInt.toString(),
|
|
167
|
+
aeth: parseFloat(aeth),
|
|
168
|
+
rpc: options.rpc,
|
|
169
|
+
timestamp: new Date().toISOString(),
|
|
170
|
+
}, null, 2));
|
|
171
|
+
} else {
|
|
172
|
+
const shortAddr = shortenAddress(address);
|
|
173
|
+
console.log();
|
|
174
|
+
console.log(`${C.bright}Account Balance${C.reset}`);
|
|
175
|
+
console.log(`${C.dim}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}`);
|
|
176
|
+
console.log(`${C.cyan}Address:${C.reset} ${C.bright}${address}${C.reset}`);
|
|
177
|
+
console.log();
|
|
178
|
+
|
|
179
|
+
if (showLamports) {
|
|
180
|
+
console.log(`${C.green}${C.bright}${balanceBigInt.toString()}${C.reset} ${C.dim}lamports${C.reset}`);
|
|
181
|
+
} else {
|
|
182
|
+
console.log(`${C.green}${C.bright}${aeth}${C.reset} ${C.dim}AETH${C.reset}`);
|
|
183
|
+
console.log(`${C.dim}(${balanceBigInt.toString()} lamports)${C.reset}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.log();
|
|
187
|
+
console.log(`${C.dim}RPC: ${options.rpc}${C.reset}`);
|
|
188
|
+
console.log(`${C.dim}Time: ${new Date().toLocaleString()}${C.reset}`);
|
|
189
|
+
console.log();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { address, lamports: balanceBigInt, aeth: parseFloat(aeth) };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Main command
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
async function balanceCommand() {
|
|
200
|
+
const options = parseArgs();
|
|
201
|
+
let address = options.address;
|
|
202
|
+
|
|
203
|
+
// If no address provided, try to use default wallet
|
|
204
|
+
if (!address) {
|
|
205
|
+
const config = loadConfig();
|
|
206
|
+
if (config.defaultWallet) {
|
|
207
|
+
address = config.defaultWallet;
|
|
208
|
+
if (!options.asJson) {
|
|
209
|
+
console.log(`${C.dim}Using default wallet: ${shortenAddress(address)}${C.reset}`);
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
const error = new Error('No address provided and no default wallet configured');
|
|
213
|
+
error.isValidationError = true;
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const client = createClient(options.rpc);
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
await fetchBalance(client, address, options);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
// Add context to error
|
|
224
|
+
if (!error.context) {
|
|
225
|
+
error.context = { address, rpc: options.rpc };
|
|
226
|
+
}
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Run with error handling
|
|
232
|
+
if (require.main === module) {
|
|
233
|
+
withErrorHandling(balanceCommand, { exit: true, verbose: process.env.AETHER_VERBOSE === '1' })();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = {
|
|
237
|
+
balanceCommand,
|
|
238
|
+
parseArgs,
|
|
239
|
+
fetchBalance,
|
|
240
|
+
loadConfig,
|
|
241
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* aether-cli blockhash
|
|
4
|
+
*
|
|
5
|
+
* Fetches the latest recent blockhash from the Aether blockchain — required
|
|
6
|
+
* as a prerequisite for signing and submitting any transaction.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* aether-cli blockhash Show latest blockhash
|
|
10
|
+
* aether-cli blockhash --json JSON output for scripting
|
|
11
|
+
* aether-cli blockhash --rpc <url> Query a specific RPC endpoint
|
|
12
|
+
* aether-cli blockhash --watch Poll every 5 seconds
|
|
13
|
+
*
|
|
14
|
+
* Requires AETHER_RPC env var (default: http://127.0.0.1:8899)
|
|
15
|
+
* SDK: @jellylegsai/aether-sdk — makes REAL HTTP RPC calls to the chain
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
// ANSI colours
|
|
21
|
+
const C = {
|
|
22
|
+
reset: '\x1b[0m',
|
|
23
|
+
bright: '\x1b[1m',
|
|
24
|
+
dim: '\x1b[2m',
|
|
25
|
+
red: '\x1b[31m',
|
|
26
|
+
green: '\x1b[32m',
|
|
27
|
+
yellow: '\x1b[33m',
|
|
28
|
+
cyan: '\x1b[36m',
|
|
29
|
+
magenta: '\x1b[35m',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Import SDK — REAL blockchain RPC calls to http://127.0.0.1:8899
|
|
33
|
+
const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
|
|
34
|
+
const aether = require(sdkPath);
|
|
35
|
+
|
|
36
|
+
const DEFAULT_RPC = process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Argument parsing
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
function parseArgs() {
|
|
43
|
+
const args = process.argv.slice(2);
|
|
44
|
+
const options = {
|
|
45
|
+
rpc: DEFAULT_RPC,
|
|
46
|
+
asJson: false,
|
|
47
|
+
watch: false,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < args.length; i++) {
|
|
51
|
+
if (args[i] === '--rpc' || args[i] === '-r') {
|
|
52
|
+
options.rpc = args[++i];
|
|
53
|
+
} else if (args[i] === '--json' || args[i] === '-j') {
|
|
54
|
+
options.asJson = true;
|
|
55
|
+
} else if (args[i] === '--watch' || args[i] === '-w') {
|
|
56
|
+
options.watch = true;
|
|
57
|
+
} else if (args[i] === '--help' || args[i] === '-h') {
|
|
58
|
+
showHelp();
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return options;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function showHelp() {
|
|
67
|
+
console.log(`
|
|
68
|
+
${C.bright}${C.cyan}aether-cli blockhash${C.reset} - Get Latest Blockhash
|
|
69
|
+
|
|
70
|
+
${C.bright}Usage:${C.reset}
|
|
71
|
+
aether-cli blockhash [options]
|
|
72
|
+
|
|
73
|
+
${C.bright}Options:${C.reset}
|
|
74
|
+
--rpc <url> Query a specific RPC endpoint (default: http://127.0.0.1:8899)
|
|
75
|
+
--json, -j Output raw JSON for scripting
|
|
76
|
+
--watch, -w Poll every 5 seconds
|
|
77
|
+
--help, -h Show this help message
|
|
78
|
+
|
|
79
|
+
${C.bright}Description:${C.reset}
|
|
80
|
+
Fetches the most recent blockhash from the Aether blockchain.
|
|
81
|
+
Blockhashes are required as a recent-ref entry when building
|
|
82
|
+
signed transactions (Transfer, Stake, Unstake, etc.).
|
|
83
|
+
|
|
84
|
+
Every call makes a REAL HTTP RPC request to the configured
|
|
85
|
+
Aether node — no caching, no stubs.
|
|
86
|
+
|
|
87
|
+
${C.bright}Examples:${C.reset}
|
|
88
|
+
aether blockhash
|
|
89
|
+
aether blockhash --json
|
|
90
|
+
aether blockhash --watch
|
|
91
|
+
AETHER_RPC=https://my-node:8899 aether blockhash
|
|
92
|
+
`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Fetch and display blockhash from REAL chain via SDK
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
async function fetchBlockhash(rpc) {
|
|
100
|
+
const client = new aether.AetherClient({ rpcUrl: rpc });
|
|
101
|
+
const result = await client.getRecentBlockhash();
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function printBlockhash(result, rpc) {
|
|
106
|
+
const { blockhash, lastValidBlockHeight } = result;
|
|
107
|
+
|
|
108
|
+
console.log(`\n${C.bright}${C.cyan}── Aether Blockhash ─────────────────────────────────────${C.reset}\n`);
|
|
109
|
+
console.log(` ${C.bright}Blockhash:${C.reset} ${C.magenta}${blockhash}${C.reset}`);
|
|
110
|
+
if (lastValidBlockHeight !== undefined) {
|
|
111
|
+
console.log(` ${C.bright}Last Valid Block Height:${C.reset} ${C.cyan}${lastValidBlockHeight.toLocaleString()}${C.reset}`);
|
|
112
|
+
}
|
|
113
|
+
console.log(` ${C.dim}RPC: ${rpc}${C.reset}\n`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function printJson(result, rpc) {
|
|
117
|
+
console.log(JSON.stringify({
|
|
118
|
+
blockhash: result.blockhash,
|
|
119
|
+
lastValidBlockHeight: result.lastValidBlockHeight,
|
|
120
|
+
rpc,
|
|
121
|
+
cli_version: '1.0.0',
|
|
122
|
+
timestamp: new Date().toISOString(),
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Main
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
async function main() {
|
|
131
|
+
const opts = parseArgs();
|
|
132
|
+
const { rpc, asJson, watch } = opts;
|
|
133
|
+
|
|
134
|
+
if (watch && !asJson) {
|
|
135
|
+
console.log(`${C.dim}Watching for new blockhashes every 5s (Ctrl+C to stop)…${C.reset}`);
|
|
136
|
+
console.log();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function tick() {
|
|
140
|
+
try {
|
|
141
|
+
const result = await fetchBlockhash(rpc);
|
|
142
|
+
if (asJson) {
|
|
143
|
+
printJson(result, rpc);
|
|
144
|
+
} else if (!watch) {
|
|
145
|
+
printBlockhash(result, rpc);
|
|
146
|
+
} else {
|
|
147
|
+
process.stdout.write(`\r${C.dim}[${new Date().toISOString()}]${C.reset} blockhash: ${C.magenta}${result.blockhash}${C.reset} `);
|
|
148
|
+
}
|
|
149
|
+
} catch (err) {
|
|
150
|
+
if (asJson) {
|
|
151
|
+
console.log(JSON.stringify({ error: err.message, rpc }));
|
|
152
|
+
} else if (!watch) {
|
|
153
|
+
console.log(`${C.red}✗ Failed to fetch blockhash: ${err.message}${C.reset}`);
|
|
154
|
+
} else {
|
|
155
|
+
process.stdout.write(`\r${C.red}✗ ${err.message}${C.reset} `);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (watch) {
|
|
161
|
+
await tick(); // immediate first run
|
|
162
|
+
// eslint-disable-next-line no-unmodified-loop-condition
|
|
163
|
+
while (true) {
|
|
164
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
165
|
+
await tick();
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
await tick();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
main().catch(err => {
|
|
173
|
+
console.error(`${C.red}✗ Blockhash command failed:${C.reset} ${err.message}`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
module.exports = { blockhashCommand: main };
|
|
178
|
+
|
|
179
|
+
if (require.main === module) {
|
|
180
|
+
main();
|
|
181
|
+
}
|