lotus-sdk
Version:
Central repository for several classes of tools for integrating with, and building for, the Lotusia ecosystem
366 lines (365 loc) • 11 kB
JavaScript
import { NODE_GEOIP_URL, PlatformURL } from './constants.js';
import * as Bitcore from '../lib/bitcore/index.js';
export async function* toAsyncIterable(collection) {
for (const item of collection) {
yield item;
}
}
export async function getGeoIP(ip) {
const response = await fetch(`${NODE_GEOIP_URL}/${ip}`);
const json = (await response.json());
return json.success ? json.data : {};
}
export function isSha256(str) {
return isHex(str, 64);
}
export function toHex(data) {
switch (typeof data) {
case 'number':
return data.toString(16).padStart(2, '0');
case 'string':
return Buffer.from(data, 'utf8').toString('hex');
case 'object':
if (data instanceof Buffer) {
return data.toString('hex');
}
}
throw new Error('Invalid data type');
}
export function isHex(str, length) {
const regexStr = length ? `^[a-fA-F0-9]{${length}}$` : '^[a-fA-F0-9]+$';
return new RegExp(regexStr).test(str);
}
export function isBase64(str) {
return new RegExp('^[a-zA-Z0-9+/]+={0,2}$').test(str);
}
export function decodeBase64(str) {
if (!isBase64(str)) {
throw new Error('Invalid base64 string');
}
return Buffer.from(str, 'base64').toString('utf8');
}
export function encodeBase64(str, encoding = 'utf8') {
if (!new TextDecoder('utf8').decode(Buffer.from(str, encoding))) {
throw new Error('Not a valid UTF-8 string');
}
return Buffer.from(str, encoding).toString('base64');
}
export function toXPIFromSats(sats) {
return Number(sats) / 1_000_000;
}
export function toSatsFromXPI(xpi) {
return Number(xpi) * 1_000_000;
}
export function truncateSha256(sha256) {
return sha256.slice(0, 16) + '...' + sha256.slice(-6);
}
export function truncateTxid(txid) {
return txid.slice(0, 16) + '...' + txid.slice(-6);
}
export function truncateAddress(address) {
return address.slice(0, 17) + '...' + address.slice(-6);
}
export function truncateBlockHash(blockHash) {
return blockHash.slice(0, 1) + '...' + blockHash.slice(-16);
}
export function numBlocksFromTip(tipHeight, blockHeight) {
return tipHeight - blockHeight + 1;
}
export function formatTimestamp(timestamp) {
const date = new Date(Number(timestamp) * 1000);
return (date.toLocaleString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: 'UTC',
}) + ' UTC');
}
export function toMinifiedPercent(positive, negative) {
const positiveNum = BigInt(positive);
const negativeNum = BigInt(negative);
if (positiveNum === 0n && negativeNum === 0n) {
return '0';
}
if (positiveNum === 0n && negativeNum > 0n) {
return '0';
}
if (positiveNum > 0n && negativeNum === 0n) {
return '100';
}
const total = positiveNum + negativeNum;
const percent = (Number(positiveNum) / Number(total)) * 100;
return percent.toFixed(1);
}
export function toPercentColor(percentage) {
const num = parseFloat(percentage);
if (num <= 100 && num >= 90) {
return 'green';
}
else if (num < 90 && num >= 80) {
return 'lime';
}
else if (num < 80 && num >= 70) {
return 'yellow';
}
else if (num < 70 && num >= 60) {
return 'amber';
}
else if (num < 60 && num >= 50) {
return 'orange';
}
else {
return 'red';
}
}
export function toMinifiedNumber(type, number) {
let unit;
switch (type) {
case 'hashrate':
unit = 'H';
break;
case 'blocksize':
unit = 'B';
break;
}
const num = Number(number);
if (isNaN(num)) {
return number.toString();
}
switch (true) {
case num >= 1_000_000_000_000_000:
return `${(num / 1_000_000_000_000_000).toFixed(1)} P${unit}`;
case num >= 1_000_000_000_000:
return `${(num / 1_000_000_000_000).toFixed(1)} T${unit}`;
case num >= 1_000_000_000:
return `${(num / 1_000_000_000).toFixed(1)} G${unit}`;
case num >= 1_000_000:
return `${(num / 1_000_000).toFixed(1)} M${unit}`;
case num >= 1_000:
return `${(num / 1000).toFixed(1)} K${unit}`;
default:
return `${num} ${unit}`;
}
}
export function toMinifiedTime(seconds) {
const num = Number(seconds);
if (isNaN(num)) {
return seconds.toString();
}
switch (true) {
case num >= 3600:
return `${(num / 3600).toFixed(1)} hours`;
case num >= 60:
return `${(num / 60).toFixed(1)} minutes`;
default:
return `${num.toFixed(1)} seconds`;
}
}
export function getRankingColor(change) {
return change > 0 ? 'green' : change < 0 ? 'red' : 'gray';
}
export function getSentimentColor(sentiment) {
switch (sentiment) {
case 'positive':
return 'green';
case 'negative':
return 'red';
case 'neutral':
return 'gray';
}
}
export function calculateRate(current, previous, divisor = 1_000_000) {
return ((current - previous) / divisor) * 100;
}
export function formatRate(rate) {
if (!isFinite(rate))
return 'New';
return `${Math.abs(rate).toFixed(1)}%`;
}
export function toUppercaseFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
export function toTrendingIcon(sentiment) {
return sentiment === 'positive'
? 'i-mdi-arrow-up-thin'
: 'i-mdi-arrow-down-thin';
}
export function toProfileUrl(platform, profileId) {
return `/${platform}/${profileId}`;
}
export function toPostUrl(platform, profileId, postId) {
return `/${platform}/${profileId}/${postId}`;
}
export function toExternalPostUrl(platform, profileId, postId) {
return PlatformURL[platform]?.post(profileId, postId);
}
export function toMinifiedStatCount(number, divisor = 1_000_000) {
number = Math.floor(Number(number) / divisor);
if (number >= 1e9) {
return `${(number / 1e9).toFixed(1)}B`;
}
else if (number >= 1e6) {
return `${(number / 1e6).toFixed(1)}M`;
}
else if (number >= 1e3) {
return `${(number / 1e3).toFixed(1)}K`;
}
else if (number <= -1e3) {
return `${(number / 1e3).toFixed(1)}K`;
}
else if (number <= -1e6) {
return `${(number / 1e6).toFixed(1)}M`;
}
else if (number <= -1e9) {
return `${(number / 1e9).toFixed(1)}B`;
}
return `${number}`;
}
export function truncatePostId(postId) {
return postId.length > 8 ? `${postId.substring(0, 8)}...` : postId;
}
export const Util = {
sha256: {
validate(str) {
return str.match(/^[a-f0-9]{64}$/);
},
},
base64: {
encode(str) {
return Buffer.from(str).toString('base64');
},
decode(str) {
if (!isBase64(str)) {
throw new Error('Invalid base64 string');
}
return Buffer.from(str, 'base64').toString('utf8');
},
},
crypto: {
randomUUID() {
return crypto.randomUUID();
},
},
};
export function createWallet(mnemonic, path) {
if (mnemonic) {
if (typeof mnemonic === 'string') {
mnemonic = new Bitcore.Mnemonic(mnemonic);
}
}
else {
mnemonic = new Bitcore.Mnemonic();
}
if (!path) {
path = "m/44'/10605'/0'/0/0";
}
const hdPrivateKey = mnemonic.toHDPrivateKey();
const privateKey = hdPrivateKey.deriveChild(path).privateKey;
const publicKey = privateKey.publicKey;
const address = privateKey.toAddress(Bitcore.Networks.mainnet);
const script = Bitcore.Script.fromAddress(address);
return {
hdPrivateKey: hdPrivateKey.toString(),
privateKey: privateKey.toWIF(),
publicKey: publicKey.toString(),
address: address,
script: script,
scriptType: script.getType(),
scriptPayload: script.getData().toString('hex'),
};
}
export function yieldToEventLoop() {
return new Promise(resolve => {
if (typeof window !== 'undefined') {
if (typeof queueMicrotask !== 'undefined') {
queueMicrotask(resolve);
}
else if (typeof MessageChannel !== 'undefined') {
const channel = new MessageChannel();
channel.port1.onmessage = () => {
channel.port1.close();
channel.port2.close();
resolve();
};
channel.port2.postMessage(null);
}
else if (typeof setTimeout !== 'undefined') {
setTimeout(resolve, 0);
}
else {
resolve();
}
}
else if (typeof setImmediate !== 'undefined') {
setImmediate(resolve);
}
else if (typeof process !== 'undefined' &&
typeof process.nextTick === 'function') {
process.nextTick(resolve);
}
else if (typeof setTimeout !== 'undefined') {
setTimeout(resolve, 0);
}
else {
resolve();
}
});
}
export function scheduleNextTick(fn) {
if (typeof window !== 'undefined') {
if (typeof queueMicrotask !== 'undefined') {
queueMicrotask(fn);
}
else if (typeof MessageChannel !== 'undefined') {
const channel = new MessageChannel();
channel.port1.onmessage = () => {
channel.port1.close();
channel.port2.close();
fn();
};
channel.port2.postMessage(null);
}
else if (typeof setTimeout !== 'undefined') {
setTimeout(fn, 0);
}
else {
fn();
}
}
else if (typeof setImmediate !== 'undefined') {
setImmediate(fn);
}
else if (typeof process !== 'undefined' &&
typeof process.nextTick === 'function') {
process.nextTick(fn);
}
else if (typeof setTimeout !== 'undefined') {
setTimeout(fn, 0);
}
else {
fn();
}
}
export function getHighResTime() {
if (typeof performance !== 'undefined' && performance.now) {
return performance.now();
}
else if (typeof process !== 'undefined' && process.hrtime) {
const [seconds, nanoseconds] = process.hrtime();
return seconds * 1000 + nanoseconds / 1e6;
}
else {
return Date.now();
}
}
export function isBrowser() {
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
export function isNode() {
return !!(typeof process !== 'undefined' &&
process.versions &&
process.versions.node);
}