UNPKG

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
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); }