aether-hub 1.2.8 → 1.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/commands/claim.js +258 -292
- package/commands/delegations.js +412 -412
- package/commands/emergency.js +607 -667
- package/commands/multisig.js +47 -88
- package/commands/rewards.js +479 -866
- package/commands/stake-positions.js +205 -205
- package/commands/tx-history.js +346 -346
- package/index.js +31 -41
- package/package.json +3 -1
- package/sdk/README.md +210 -0
- package/sdk/index.js +1013 -0
- package/sdk/package.json +33 -0
- package/sdk/rpc.js +108 -0
- package/sdk/test.js +85 -0
- package/theme.js +211 -0
package/sdk/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jellylegsai/aether-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Aether Blockchain SDK - Real HTTP RPC calls to Aether chain",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js",
|
|
9
|
+
"rpc.js",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "node test.js",
|
|
14
|
+
"build": "node -e \"require('./index.js'); console.log('SDK build OK')\""
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"aether",
|
|
18
|
+
"blockchain",
|
|
19
|
+
"sdk",
|
|
20
|
+
"crypto",
|
|
21
|
+
"rpc",
|
|
22
|
+
"web3"
|
|
23
|
+
],
|
|
24
|
+
"author": "Jelly-legs AI Team",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/jelly-legs-ai/Jelly-legs-unsteady-workshop"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=14.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/sdk/rpc.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @jellylegsai/aether-sdk - RPC Client
|
|
4
|
+
*
|
|
5
|
+
* Low-level HTTP RPC client for Aether blockchain.
|
|
6
|
+
* All functions make REAL HTTP calls to the blockchain RPC endpoint.
|
|
7
|
+
* No stubs, no mocks.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const http = require('http');
|
|
11
|
+
const https = require('https');
|
|
12
|
+
|
|
13
|
+
const DEFAULT_RPC = process.env.AETHER_RPC || 'http://127.0.0.1:8899';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Make a GET request to the RPC endpoint
|
|
17
|
+
* @param {string} rpcUrl - RPC endpoint URL
|
|
18
|
+
* @param {string} path - API path (e.g., /v1/slot)
|
|
19
|
+
* @param {number} timeout - Request timeout in ms
|
|
20
|
+
* @returns {Promise<object>} Parsed JSON response
|
|
21
|
+
*/
|
|
22
|
+
function rpcGet(rpcUrl, path, timeout = 8000) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const url = new URL(path, rpcUrl);
|
|
25
|
+
const isHttps = url.protocol === 'https:';
|
|
26
|
+
const lib = isHttps ? https : http;
|
|
27
|
+
|
|
28
|
+
const req = lib.request({
|
|
29
|
+
hostname: url.hostname,
|
|
30
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
31
|
+
path: url.pathname + url.search,
|
|
32
|
+
method: 'GET',
|
|
33
|
+
timeout,
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
}, (res) => {
|
|
36
|
+
let data = '';
|
|
37
|
+
res.on('data', (chunk) => (data += chunk));
|
|
38
|
+
res.on('end', () => {
|
|
39
|
+
try {
|
|
40
|
+
resolve(JSON.parse(data));
|
|
41
|
+
} catch {
|
|
42
|
+
resolve({ raw: data });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
req.on('error', reject);
|
|
48
|
+
req.on('timeout', () => {
|
|
49
|
+
req.destroy();
|
|
50
|
+
reject(new Error(`RPC request timeout after ${timeout}ms`));
|
|
51
|
+
});
|
|
52
|
+
req.end();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Make a POST request to the RPC endpoint
|
|
58
|
+
* @param {string} rpcUrl - RPC endpoint URL
|
|
59
|
+
* @param {string} path - API path
|
|
60
|
+
* @param {object} body - Request body (will be JSON.stringify'd)
|
|
61
|
+
* @param {number} timeout - Request timeout in ms
|
|
62
|
+
* @returns {Promise<object>} Parsed JSON response
|
|
63
|
+
*/
|
|
64
|
+
function rpcPost(rpcUrl, path, body, timeout = 8000) {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const url = new URL(path, rpcUrl);
|
|
67
|
+
const isHttps = url.protocol === 'https:';
|
|
68
|
+
const lib = isHttps ? https : http;
|
|
69
|
+
const bodyStr = JSON.stringify(body);
|
|
70
|
+
|
|
71
|
+
const req = lib.request({
|
|
72
|
+
hostname: url.hostname,
|
|
73
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
74
|
+
path: url.pathname + url.search,
|
|
75
|
+
method: 'POST',
|
|
76
|
+
timeout,
|
|
77
|
+
headers: {
|
|
78
|
+
'Content-Type': 'application/json',
|
|
79
|
+
'Content-Length': Buffer.byteLength(bodyStr),
|
|
80
|
+
},
|
|
81
|
+
}, (res) => {
|
|
82
|
+
let data = '';
|
|
83
|
+
res.on('data', (chunk) => (data += chunk));
|
|
84
|
+
res.on('end', () => {
|
|
85
|
+
try {
|
|
86
|
+
resolve(JSON.parse(data));
|
|
87
|
+
} catch {
|
|
88
|
+
resolve(data);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
req.on('error', reject);
|
|
94
|
+
req.on('timeout', () => {
|
|
95
|
+
req.destroy();
|
|
96
|
+
reject(new Error(`RPC request timeout after ${timeout}ms`));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
req.write(bodyStr);
|
|
100
|
+
req.end();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
rpcGet,
|
|
106
|
+
rpcPost,
|
|
107
|
+
DEFAULT_RPC,
|
|
108
|
+
};
|
package/sdk/test.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @jellylegsai/aether-sdk - Test Script
|
|
4
|
+
*
|
|
5
|
+
* Tests all SDK functions with REAL RPC calls to http://127.0.0.1:8899
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const aether = require('./index');
|
|
9
|
+
|
|
10
|
+
const C = {
|
|
11
|
+
reset: '\x1b[0m',
|
|
12
|
+
green: '\x1b[32m',
|
|
13
|
+
red: '\x1b[31m',
|
|
14
|
+
yellow: '\x1b[33m',
|
|
15
|
+
cyan: '\x1b[36m',
|
|
16
|
+
dim: '\x1b[2m',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function test(name, fn) {
|
|
20
|
+
try {
|
|
21
|
+
const result = await fn();
|
|
22
|
+
console.log(` ${C.green}✓${C.reset} ${name}`);
|
|
23
|
+
return { ok: true, result };
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.log(` ${C.red}✗${C.reset} ${name}: ${err.message}`);
|
|
26
|
+
return { ok: false, error: err.message };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function main() {
|
|
31
|
+
console.log(`\n${C.cyan}══ @jellylegsai/aether-sdk Test Suite ══${C.reset}\n`);
|
|
32
|
+
console.log(` ${C.dim}RPC Endpoint: ${aether.DEFAULT_RPC_URL}${C.reset}\n`);
|
|
33
|
+
|
|
34
|
+
const results = [];
|
|
35
|
+
|
|
36
|
+
// Test ping first
|
|
37
|
+
results.push(await test('ping()', async () => {
|
|
38
|
+
const ping = await aether.ping();
|
|
39
|
+
if (!ping.ok) throw new Error(ping.error);
|
|
40
|
+
return ping;
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
// Test chain queries
|
|
44
|
+
results.push(await test('getSlot()', () => aether.getSlot()));
|
|
45
|
+
results.push(await test('getBlockHeight()', () => aether.getBlockHeight()));
|
|
46
|
+
results.push(await test('getEpoch()', () => aether.getEpoch()));
|
|
47
|
+
results.push(await test('getTPS()', () => aether.getTPS()));
|
|
48
|
+
results.push(await test('getSupply()', () => aether.getSupply()));
|
|
49
|
+
results.push(await test('getFees()', () => aether.getFees()));
|
|
50
|
+
results.push(await test('getValidators()', () => aether.getValidators()));
|
|
51
|
+
results.push(await test('getPeers()', () => aether.getPeers()));
|
|
52
|
+
results.push(await test('getHealth()', () => aether.getHealth()));
|
|
53
|
+
results.push(await test('getSlotProduction()', () => aether.getSlotProduction()));
|
|
54
|
+
|
|
55
|
+
// Test with a sample address (may not exist, but tests the call)
|
|
56
|
+
const testAddress = 'ATH111111111111111111111111111111111';
|
|
57
|
+
results.push(await test(`getAccount('${testAddress.substring(0, 12)}...')`, () =>
|
|
58
|
+
aether.getAccount(testAddress)
|
|
59
|
+
));
|
|
60
|
+
results.push(await test(`getBalance('${testAddress.substring(0, 12)}...')`, () =>
|
|
61
|
+
aether.getBalance(testAddress)
|
|
62
|
+
));
|
|
63
|
+
results.push(await test(`getStakePositions('${testAddress.substring(0, 12)}...')`, () =>
|
|
64
|
+
aether.getStakePositions(testAddress)
|
|
65
|
+
));
|
|
66
|
+
results.push(await test(`getRewards('${testAddress.substring(0, 12)}...')`, () =>
|
|
67
|
+
aether.getRewards(testAddress)
|
|
68
|
+
));
|
|
69
|
+
|
|
70
|
+
// Summary
|
|
71
|
+
const passed = results.filter(r => r.ok).length;
|
|
72
|
+
const failed = results.filter(r => !r.ok).length;
|
|
73
|
+
|
|
74
|
+
console.log();
|
|
75
|
+
if (failed === 0) {
|
|
76
|
+
console.log(` ${C.green}══ All ${passed} tests passed! ══${C.reset}\n`);
|
|
77
|
+
} else {
|
|
78
|
+
console.log(` ${C.yellow}══ ${passed} passed, ${failed} failed ══${C.reset}`);
|
|
79
|
+
console.log(` ${C.dim} (Failures may be due to RPC not running or test data not existing)${C.reset}\n`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
main();
|
package/theme.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aether-cli - Shared Theme & Branding
|
|
3
|
+
*
|
|
4
|
+
* Consistent colors, ASCII art, and styling across all CLI commands.
|
|
5
|
+
* Import this module to ensure unified visual identity.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ANSI color codes - Aether Brand Palette
|
|
9
|
+
const COLORS = {
|
|
10
|
+
// Reset
|
|
11
|
+
reset: '\x1b[0m',
|
|
12
|
+
|
|
13
|
+
// Styles
|
|
14
|
+
bright: '\x1b[1m',
|
|
15
|
+
dim: '\x1b[2m',
|
|
16
|
+
italic: '\x1b[3m',
|
|
17
|
+
underline: '\x1b[4m',
|
|
18
|
+
|
|
19
|
+
// Core palette
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
green: '\x1b[32m',
|
|
22
|
+
yellow: '\x1b[33m',
|
|
23
|
+
blue: '\x1b[34m',
|
|
24
|
+
magenta: '\x1b[35m',
|
|
25
|
+
cyan: '\x1b[36m',
|
|
26
|
+
white: '\x1b[37m',
|
|
27
|
+
|
|
28
|
+
// Extended palette
|
|
29
|
+
crimson: '\x1b[38;5;161m',
|
|
30
|
+
gold: '\x1b[38;5;220m',
|
|
31
|
+
lime: '\x1b[38;5;118m',
|
|
32
|
+
orange: '\x1b[38;5;208m',
|
|
33
|
+
purple: '\x1b[38;5;93m',
|
|
34
|
+
teal: '\x1b[38;5;6m',
|
|
35
|
+
|
|
36
|
+
// Tier colors
|
|
37
|
+
full: '\x1b[36m', // cyan - Full validator
|
|
38
|
+
lite: '\x1b[33m', // yellow - Lite validator
|
|
39
|
+
observer: '\x1b[32m', // green - Observer
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Short alias for convenience
|
|
43
|
+
const C = COLORS;
|
|
44
|
+
|
|
45
|
+
// ASCII Art Banners
|
|
46
|
+
const BANNERS = {
|
|
47
|
+
// Main CLI banner
|
|
48
|
+
main: `
|
|
49
|
+
${C.cyan} _ _____ _ _ ________ ______ _ _ ${C.reset}
|
|
50
|
+
${C.cyan} / \\ | ____| | | |__ /_ _| |__ /| | | |${C.reset}
|
|
51
|
+
${C.cyan} / _ \\ | _| | |_| | / / | | / /| |_| |${C.reset}
|
|
52
|
+
${C.cyan} / ___ \\| |___| _ |/ /_ | | / /_| _ |${C.reset}
|
|
53
|
+
${C.cyan}/_/ \\_\\_____|_| |_/____|___| /____|_| |_|${C.reset}
|
|
54
|
+
${C.dim} Validator Command Line Interface${C.reset}
|
|
55
|
+
`,
|
|
56
|
+
|
|
57
|
+
// Compact banner for subcommands
|
|
58
|
+
compact: `
|
|
59
|
+
${C.cyan}╔═══════════════════════════════════════════════════════════════╗${C.reset}
|
|
60
|
+
${C.cyan}║${C.reset} ${C.bright}AETHER CHAIN — Validator CLI${C.reset}${C.cyan} ║${C.reset}
|
|
61
|
+
${C.cyan}╚═══════════════════════════════════════════════════════════════╝${C.reset}`,
|
|
62
|
+
|
|
63
|
+
// Section headers
|
|
64
|
+
network: `${C.cyan}╔═══════════════════════════════════════════════════════════════════╗${C.reset}
|
|
65
|
+
${C.cyan}║${C.reset} ${C.bright}AETHER NETWORK STATUS${C.reset}${C.cyan} ║${C.reset}
|
|
66
|
+
${C.cyan}╚═══════════════════════════════════════════════════════════════════╝${C.reset}`,
|
|
67
|
+
|
|
68
|
+
balance: `${C.bright}${C.cyan}── Aether Account Balance ───────────────────────────────${C.reset}`,
|
|
69
|
+
slot: `${C.bright}${C.cyan}── Aether Current Slot ──────────────────────────────────${C.reset}`,
|
|
70
|
+
tps: `${C.bright}${C.cyan}── Aether Network TPS ─────────────────────────────────${C.reset}`,
|
|
71
|
+
fees: `${C.bright}${C.cyan}── Aether Network Fees ──────────────────────────────────${C.reset}`,
|
|
72
|
+
stake: `${C.bright}${C.cyan}── Aether Stake Info ─────────────────────────────────${C.reset}`,
|
|
73
|
+
rewards: `${C.bright}${C.cyan}╔═══════════════════════════════════════════════════════╗${C.reset}
|
|
74
|
+
${C.bright}${C.cyan}║ Staking Rewards ║${C.reset}
|
|
75
|
+
${C.bright}${C.cyan}╚═══════════════════════════════════════════════════════╝${C.reset}`,
|
|
76
|
+
claim: `${C.bright}${C.cyan}── Claim Staking Rewards ────────────────────────────────${C.reset}`,
|
|
77
|
+
emergency: `${C.bright}${C.cyan}🔔 Aether Emergency Status${C.reset}`,
|
|
78
|
+
multisig: `${C.bright}${C.cyan}── Multi-Signature Wallet ─────────────────────────${C.reset}`,
|
|
79
|
+
validator: `${C.bright}${C.cyan}── Validator Management ─────────────────────────────${C.reset}`,
|
|
80
|
+
wallet: `${C.bright}${C.cyan}── Wallet Management ────────────────────────────────${C.reset}`,
|
|
81
|
+
txHistory: `${C.bright}${C.cyan} Transaction History${C.reset}`,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Status icons
|
|
85
|
+
const ICONS = {
|
|
86
|
+
success: `${C.green}✓${C.reset}`,
|
|
87
|
+
error: `${C.red}✗${C.reset}`,
|
|
88
|
+
warning: `${C.yellow}⚠${C.reset}`,
|
|
89
|
+
info: `${C.cyan}ℹ${C.reset}`,
|
|
90
|
+
pending: `${C.yellow}⏳${C.reset}`,
|
|
91
|
+
active: `${C.green}●${C.reset}`,
|
|
92
|
+
inactive: `${C.dim}○${C.reset}`,
|
|
93
|
+
bullet: `${C.cyan}★${C.reset}`,
|
|
94
|
+
arrow: `${C.cyan}▸${C.reset}`,
|
|
95
|
+
lightning: `${C.yellow}⚡${C.reset}`,
|
|
96
|
+
fire: `${C.red}🔥${C.reset}`,
|
|
97
|
+
bell: `${C.yellow}🔔${C.reset}`,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Box drawing characters
|
|
101
|
+
const BOX = {
|
|
102
|
+
h: '─',
|
|
103
|
+
v: '│',
|
|
104
|
+
tl: '┌',
|
|
105
|
+
tr: '┐',
|
|
106
|
+
bl: '└',
|
|
107
|
+
br: '┘',
|
|
108
|
+
t: '┬',
|
|
109
|
+
b: '┴',
|
|
110
|
+
l: '├',
|
|
111
|
+
r: '┤',
|
|
112
|
+
cross: '┼',
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// CLI Metadata
|
|
116
|
+
const META = {
|
|
117
|
+
version: '1.3.0',
|
|
118
|
+
name: 'aether-hub',
|
|
119
|
+
description: 'AeTHer Validator CLI — tiered validators, system checks, and node management',
|
|
120
|
+
author: 'Jelly-legs AI Team',
|
|
121
|
+
repository: 'https://github.com/jelly-legs-ai/Jelly-legs-unsteady-workshop',
|
|
122
|
+
rpc: {
|
|
123
|
+
default: 'http://127.0.0.1:8899',
|
|
124
|
+
env: 'AETHER_RPC',
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Helper functions
|
|
129
|
+
function printBanner(type = 'compact') {
|
|
130
|
+
console.log(BANNERS[type] || BANNERS.compact);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function printBox(content, width = 70) {
|
|
134
|
+
const lines = content.split('\n');
|
|
135
|
+
console.log(`${C.cyan}${BOX.tl}${BOX.h.repeat(width - 2)}${BOX.tr}${C.reset}`);
|
|
136
|
+
lines.forEach(line => {
|
|
137
|
+
const padded = line.padEnd(width - 4);
|
|
138
|
+
console.log(`${C.cyan}${BOX.v}${C.reset} ${padded} ${C.cyan}${BOX.v}${C.reset}`);
|
|
139
|
+
});
|
|
140
|
+
console.log(`${C.cyan}${BOX.bl}${BOX.h.repeat(width - 2)}${BOX.br}${C.reset}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function printDivider(char = '─', width = 70) {
|
|
144
|
+
console.log(`${C.dim}${char.repeat(width)}${C.reset}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function formatLabel(label, value, options = {}) {
|
|
148
|
+
const { color = C.bright, valueColor = C.reset, indent = 2 } = options;
|
|
149
|
+
const spaces = ' '.repeat(indent);
|
|
150
|
+
return `${spaces}${color}${label}:${C.reset} ${valueColor}${value}${C.reset}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function formatAeth(lamports, options = {}) {
|
|
154
|
+
const { decimals = 6, showLamports = false } = options;
|
|
155
|
+
const aeth = (Number(lamports) / 1e9).toFixed(decimals).replace(/\.?0+$/, '');
|
|
156
|
+
if (showLamports) {
|
|
157
|
+
return `${C.green}${aeth} AETH${C.reset} ${C.dim}(${Number(lamports).toLocaleString()} lamports)${C.reset}`;
|
|
158
|
+
}
|
|
159
|
+
return `${C.green}${aeth} AETH${C.reset}`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function formatDate(date) {
|
|
163
|
+
if (!date) return `${C.dim}—${C.reset}`;
|
|
164
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
165
|
+
return d.toISOString().replace('T', ' ').slice(0, 19);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function shortenAddress(addr, start = 6, end = 4) {
|
|
169
|
+
if (!addr || addr.length <= start + end + 3) return addr || `${C.dim}unknown${C.reset}`;
|
|
170
|
+
return `${addr.slice(0, start)}...${addr.slice(-end)}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getRpcUrl(customUrl = null) {
|
|
174
|
+
return customUrl || process.env[META.rpc.env] || META.rpc.default;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Status colors based on value
|
|
178
|
+
function statusColor(value, thresholds = {}) {
|
|
179
|
+
const { good = 80, warning = 50 } = thresholds;
|
|
180
|
+
if (value >= good) return C.green;
|
|
181
|
+
if (value >= warning) return C.yellow;
|
|
182
|
+
return C.red;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function tpsColor(tps) {
|
|
186
|
+
if (tps === null || tps === undefined) return C.red;
|
|
187
|
+
if (tps >= 1000) return C.green;
|
|
188
|
+
if (tps >= 100) return C.cyan;
|
|
189
|
+
if (tps >= 10) return C.yellow;
|
|
190
|
+
return C.red;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Export everything
|
|
194
|
+
module.exports = {
|
|
195
|
+
COLORS,
|
|
196
|
+
C,
|
|
197
|
+
BANNERS,
|
|
198
|
+
ICONS,
|
|
199
|
+
BOX,
|
|
200
|
+
META,
|
|
201
|
+
printBanner,
|
|
202
|
+
printBox,
|
|
203
|
+
printDivider,
|
|
204
|
+
formatLabel,
|
|
205
|
+
formatAeth,
|
|
206
|
+
formatDate,
|
|
207
|
+
shortenAddress,
|
|
208
|
+
getRpcUrl,
|
|
209
|
+
statusColor,
|
|
210
|
+
tpsColor,
|
|
211
|
+
};
|