UNPKG

blockchain-api

Version:

API utilities for interacting with the Exatechl2 blockchain

422 lines (418 loc) 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.standardizeTimestamp = standardizeTimestamp; exports.parseBlockchainTimestamp = parseBlockchainTimestamp; exports.parseTimestampToMillis = parseTimestampToMillis; exports.calculateAgeInMillis = calculateAgeInMillis; exports.formatRelativeTime = formatRelativeTime; exports.formatTableTime = formatTableTime; exports.formatNumber = formatNumber; exports.formatWeiToEth = formatWeiToEth; exports.truncateString = truncateString; exports.formatAddress = formatAddress; exports.formatTokenAmount = formatTokenAmount; exports.parseHexToDecimal = parseHexToDecimal; exports.formatGasValue = formatGasValue; exports.calculateGasPercentage = calculateGasPercentage; exports.formatDecimalToHex = formatDecimalToHex; const logger_1 = require("./logger"); /** * * @param {string|number} timestamp * @param {boolean} debug * @returns {number} */ function standardizeTimestamp(timestamp, debug = false) { if (timestamp === undefined || timestamp === null) { if (debug) logger_1.logger.debug('Timestamp undefined or null, using current time'); return Date.now(); } const timestampNum = typeof timestamp === 'string' ? parseInt(timestamp, 10) : timestamp; if (debug) logger_1.logger.debug(`Standardizing timestamp: original=${timestamp}, parsed=${timestampNum}`); if (timestampNum < 10000000000) { const converted = timestampNum * 1000; if (debug) logger_1.logger.debug(`Converting seconds to milliseconds: ${timestampNum}${converted} (${new Date(converted).toISOString()})`); return converted; } if (debug) logger_1.logger.debug(`Timestamp already in milliseconds: ${timestampNum} (${new Date(timestampNum).toISOString()})`); return timestampNum; } /** * * @param {string|number|Date} timestamp * @returns {Date} * @throws {Error} */ function parseBlockchainTimestamp(timestamp) { if (!timestamp) { throw new Error('Invalid timestamp: empty value'); } let date; if (timestamp instanceof Date) { date = timestamp; } else if (typeof timestamp === 'string') { if (timestamp.startsWith('0x')) { const seconds = parseInt(timestamp, 16); date = new Date(seconds * 1000); } else if (/^\d+$/.test(timestamp)) { const num = parseInt(timestamp, 10); if (num < 10000000000) { date = new Date(num * 1000); } else { date = new Date(num); } } else if (timestamp.includes('T') && timestamp.includes('-')) { date = new Date(timestamp); } else if (timestamp.includes('/') || timestamp.includes('-')) { date = new Date(timestamp); } else { date = new Date(timestamp); } } else { if (timestamp < 10000000000) { date = new Date(timestamp * 1000); } else { date = new Date(timestamp); } } if (isNaN(date.getTime())) { throw new Error(`Invalid timestamp: could not parse date from ${JSON.stringify(timestamp)}`); } return date; } /** * * @param {string|number|Date} timestamp * @param {boolean} fallbackToNow * @returns {number} */ function parseTimestampToMillis(timestamp, fallbackToNow = true) { try { const date = parseBlockchainTimestamp(timestamp); return date.getTime(); } catch (error) { logger_1.logger.error('Error parsing timestamp:', error, timestamp); return fallbackToNow ? Date.now() : 0; } } /** * * @param {string|number|Date} timestamp * @returns {number} */ function calculateAgeInMillis(timestamp) { const timestampMs = parseTimestampToMillis(timestamp); return Date.now() - timestampMs; } /** * * @param {string|number|Date} timestamp * @returns {string} */ function formatRelativeTime(timestamp) { if (!timestamp) { return 'Unknown time'; } let date; try { date = parseBlockchainTimestamp(timestamp); } catch (error) { logger_1.logger.error('Error parsing timestamp:', error, timestamp); return 'Invalid date'; } const now = new Date(); const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000); if (isNaN(diffInSeconds)) { return 'Invalid date'; } let relativeTime = ''; if (diffInSeconds < 5) { relativeTime = 'Just now'; } else if (diffInSeconds < 60) { relativeTime = `${diffInSeconds} ${diffInSeconds === 1 ? 'sec' : 'secs'} ago`; } else { const diffInMinutes = Math.floor(diffInSeconds / 60); if (diffInMinutes < 60) { relativeTime = `${diffInMinutes} ${diffInMinutes === 1 ? 'min' : 'mins'} ago`; } else { const diffInHours = Math.floor(diffInMinutes / 60); if (diffInHours < 24) { relativeTime = `${diffInHours} ${diffInHours === 1 ? 'hour' : 'hours'} ago`; } else { const diffInDays = Math.floor(diffInHours / 24); if (diffInDays < 30) { relativeTime = `${diffInDays} ${diffInDays === 1 ? 'day' : 'days'} ago`; } else { const diffInMonths = Math.floor(diffInDays / 30); if (diffInMonths < 12) { relativeTime = `${diffInMonths} ${diffInMonths === 1 ? 'month' : 'months'} ago`; } else { const diffInYears = Math.floor(diffInMonths / 12); relativeTime = `${diffInYears} ${diffInYears === 1 ? 'year' : 'years'} ago`; } } } } } const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const year = date.getUTCFullYear(); const month = months[date.getUTCMonth()]; const day = date.getUTCDate().toString().padStart(2, '0'); let hours = date.getUTCHours(); const ampm = hours >= 12 ? 'PM' : 'AM'; hours = hours % 12; hours = hours ? hours : 12; const minutes = date.getUTCMinutes().toString().padStart(2, '0'); const seconds = date.getUTCSeconds().toString().padStart(2, '0'); const utcTime = `${month}-${day}-${year} ${hours.toString().padStart(2, '0')}:${minutes}:${seconds} ${ampm} +UTC`; return `${relativeTime} (${utcTime})`; } /** * @param {string|number|Date} timestamp * @returns {string} */ function formatTableTime(timestamp) { if (!timestamp) { return 'Unknown time'; } try { const date = parseBlockchainTimestamp(timestamp); const now = new Date(); const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000); if (isNaN(diffInSeconds)) { return 'Invalid date'; } if (diffInSeconds < 5) { return 'Just now'; } if (diffInSeconds < 60) { return `${diffInSeconds} ${diffInSeconds === 1 ? 'sec' : 'secs'} ago`; } const diffInMinutes = Math.floor(diffInSeconds / 60); if (diffInMinutes < 60) { return `${diffInMinutes} ${diffInMinutes === 1 ? 'min' : 'mins'} ago`; } const diffInHours = Math.floor(diffInMinutes / 60); if (diffInHours < 24) { return `${diffInHours} ${diffInHours === 1 ? 'hour' : 'hours'} ago`; } const diffInDays = Math.floor(diffInHours / 24); if (diffInDays < 30) { return `${diffInDays} ${diffInDays === 1 ? 'day' : 'days'} ago`; } const diffInMonths = Math.floor(diffInDays / 30); if (diffInMonths < 12) { return `${diffInMonths} ${diffInMonths === 1 ? 'month' : 'months'} ago`; } const diffInYears = Math.floor(diffInMonths / 12); return `${diffInYears} ${diffInYears === 1 ? 'year' : 'years'} ago`; } catch (error) { logger_1.logger.error('Error parsing timestamp:', error, timestamp); return 'Invalid date'; } } /** * * @param {number} value * @param {number} decimals * @returns {string} */ function formatNumber(value, decimals = 2) { if (!value && value !== 0) return 'N/A'; if (value === 0) return '0'; if (Math.abs(value) < 1000) { return value.toFixed(decimals).replace(/\.0+$/, ''); } const suffixes = ['', 'K', 'M', 'B', 'T']; const suffixIndex = Math.floor(Math.log10(Math.abs(value)) / 3); if (suffixIndex >= suffixes.length) { return value.toExponential(decimals); } const scaledValue = value / Math.pow(10, suffixIndex * 3); return `${scaledValue.toFixed(decimals).replace(/\.0+$/, '')}${suffixes[suffixIndex]}`; } /** * * @param {string|number} wei * @param {number} decimals * @returns {string} */ function formatWeiToEth(wei, decimals = 6) { if (!wei) return '0 tEXT'; try { const weiValue = typeof wei === 'string' && wei.startsWith('0x') ? parseInt(wei, 16) : Number(wei); const ethValue = weiValue / 1e18; if (ethValue === Math.floor(ethValue)) { return `${Math.floor(ethValue)} tEXT`; } const formatted = ethValue.toFixed(decimals); const trimmed = formatted.replace(/\.?0+$/, ''); return `${trimmed} tEXT`; } catch (e) { logger_1.logger.error('Error formatting wei to tEXT:', e); return '0 tEXT'; } } /** * * @param {string} str * @param {number} frontChars * @param {number} endChars * @returns {string} */ function truncateString(str, frontChars = 6, endChars = 4) { if (!str) return ''; if (str.length <= frontChars + endChars) return str; return `${str.substring(0, frontChars)}...${str.substring(str.length - endChars)}`; } /** * * @param {string} address * @returns {string} */ function formatAddress(address) { if (!address) return ''; return truncateString(address, 6, 4); } /** * * @param {string|number} rawAmount * @param {number} decimals * @param {string} symbol * @returns {string} */ function formatTokenAmount(rawAmount, decimals = 18, symbol = '') { if (!rawAmount) return `0${symbol ? ' ' + symbol : ''}`; let numericAmount; if (typeof rawAmount === 'string') { if (rawAmount.startsWith('0x')) { numericAmount = parseInt(rawAmount, 16) / Math.pow(10, decimals); } else { numericAmount = Number(rawAmount) / Math.pow(10, decimals); } } else { numericAmount = rawAmount / Math.pow(10, decimals); } return `${numericAmount.toLocaleString(undefined, { maximumFractionDigits: numericAmount > 1000 ? 0 : 2, minimumFractionDigits: 0 })}${symbol ? ' ' + symbol : ''}`; } /** * * @param {string|number} hexValue * @param {number} defaultValue * @returns {number} */ function parseHexToDecimal(hexValue, defaultValue = 0) { try { if (hexValue === undefined || hexValue === null) { return defaultValue; } if (typeof hexValue === 'number') { return hexValue; } if (typeof hexValue === 'string') { if (hexValue.startsWith('0x')) { return parseInt(hexValue, 16); } return parseInt(hexValue, 10); } return defaultValue; } catch (error) { logger_1.logger.error('Error parsing hex value:', error, hexValue); return defaultValue; } } /** * * @param {string|number} value * @param {string} unit * @param {number} decimals * @returns {string} */ function formatGasValue(value, unit = 'gwei', decimals = 2) { try { if (value === undefined || value === null) { return 'N/A'; } const weiValue = parseHexToDecimal(value); if (weiValue === 0) { return `0 ${unit.toUpperCase()}`; } let convertedValue = weiValue; let unitLabel = unit.toUpperCase(); if (unit.toLowerCase() === 'gwei') { convertedValue = weiValue / 1e9; unitLabel = 'Gwei'; } else if (unit.toLowerCase() === 'text') { convertedValue = weiValue / 1e18; unitLabel = 'TEXT'; } else { unitLabel = 'Wei'; } return `${convertedValue.toFixed(decimals).replace(/\.0+$/, '')} ${unitLabel}`; } catch (error) { logger_1.logger.error('Error formatting gas value:', error, value); return 'N/A'; } } /** * * @param {string|number} gasUsed * @param {string|number} gasLimit * @returns {number} */ function calculateGasPercentage(gasUsed, gasLimit) { const gasUsedDecimal = parseHexToDecimal(gasUsed); const gasLimitDecimal = parseHexToDecimal(gasLimit); if (gasLimitDecimal === 0) { return 0; } return Math.round((gasUsedDecimal / gasLimitDecimal) * 100); } /** * * @param {number} value * @returns {string} */ function formatDecimalToHex(value) { return '0x' + value.toString(16); }