/** * Component: TransactionsList * Author: Nearblocks Pte Ltd * License: Business Source License 1.1 * Description: Table of Transactions on Near Protocol. * @interface Props * @param {string} [network] - The network data to show, either mainnet or testnet * @param {Function} [t] - A function for internationalization (i18n) provided by the next-translate package. * @param {number} [currentPage] - The current page number being displayed. (Optional) * Example: If provided, currentPage=3 will display the third page of blocks. * @param {function} [setPage] - A function used to set the current page. (Optional) * Example: setPage={handlePageChange} where handlePageChange is a function to update the page. * @param {Object.<string, string>} [filters] - Key-value pairs for filtering transactions. (Optional) * Example: If provided, method=batch will filter the blocks with method=batch. * @param {function} [handleFilter] - Function to handle filter changes. (Optional) * Example: handleFilter={handlePageFilter} where handlePageFilter is a function to filter the page. * @param {function} [onFilterClear] - Function to clear a specific or all filters. (Optional) * Example: onFilterClear={handleClearFilter} where handleClearFilter is a function to clear the applied filters. */ /* INCLUDE: "includes/formats.jsx" */ function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function dollarNonCentFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 0, }); return formattedNumber; } function weight(number) { const suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; let suffixIndex = 0; while (number >= 1000 && suffixIndex < suffixes.length - 1) { number /= 1000; suffixIndex++; } return number.toFixed(2) + ' ' + suffixes[suffixIndex]; } function convertToUTC(timestamp, hour) { const date = new Date(timestamp); // Get UTC date components const utcYear = date.getUTCFullYear(); const utcMonth = ('0' + (date.getUTCMonth() + 1)).slice(-2); // Adding 1 because months are zero-based const utcDay = ('0' + date.getUTCDate()).slice(-2); const utcHours = ('0' + date.getUTCHours()).slice(-2); const utcMinutes = ('0' + date.getUTCMinutes()).slice(-2); const utcSeconds = ('0' + date.getUTCSeconds()).slice(-2); // Array of month abbreviations const monthAbbreviations = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ]; const monthIndex = Number(utcMonth) - 1; // Format the date as required (Jul-25-2022 16:25:37) let formattedDate = monthAbbreviations[monthIndex] + '-' + utcDay + '-' + utcYear + ' ' + utcHours + ':' + utcMinutes + ':' + utcSeconds; if (hour) { // Convert hours to 12-hour format let hour12 = parseInt(utcHours); const ampm = hour12 >= 12 ? 'PM' : 'AM'; hour12 = hour12 % 12 || 12; // Add AM/PM to the formatted date (Jul-25-2022 4:25:37 PM) formattedDate = monthAbbreviations[monthIndex] + '-' + utcDay + '-' + utcYear + ' ' + hour12 + ':' + utcMinutes + ':' + utcSeconds + ' ' + ampm; } return formattedDate; } function getTimeAgoString(timestamp) { const currentUTC = Date.now(); const date = new Date(timestamp); const seconds = Math.floor((currentUTC - date.getTime()) / 1000); const intervals = { year: seconds / (60 * 60 * 24 * 365), month: seconds / (60 * 60 * 24 * 30), week: seconds / (60 * 60 * 24 * 7), day: seconds / (60 * 60 * 24), hour: seconds / (60 * 60), minute: seconds / 60, }; if (intervals.year == 1) { return Math.ceil(intervals.year) + ' year ago'; } else if (intervals.year > 1) { return Math.ceil(intervals.year) + ' years ago'; } else if (intervals.month > 1) { return Math.ceil(intervals.month) + ' months ago'; } else if (intervals.day > 1) { return Math.ceil(intervals.day) + ' days ago'; } else if (intervals.hour > 1) { return Math.ceil(intervals.hour) + ' hours ago'; } else if (intervals.minute > 1) { return Math.ceil(intervals.minute) + ' minutes ago'; } else { return 'a few seconds ago'; } } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function formatTimestampToString(timestamp) { const date = new Date(timestamp); // Format the date to 'YYYY-MM-DD HH:mm:ss' format const formattedDate = date.toISOString().replace('T', ' ').split('.')[0]; return formattedDate; } function convertToMetricPrefix(number) { const prefixes = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; // Metric prefixes let count = 0; while (Math.abs(number) >= 1000 && count < prefixes.length - 1) { number /= 1000; count++; } // Check if the number is close to an integer value if (Math.abs(number) >= 10) { number = Math.round(number); // Round the number to the nearest whole number return number + ' ' + prefixes[count]; } return ( Number(Math.floor(number * 100) / 100).toFixed(2) + ' ' + prefixes[count] ); } function formatNumber(value) { const suffixes = ['', 'K', 'M', 'B', 'T']; let suffixIndex = 0; while (value >= 10000 && suffixIndex < suffixes.length - 1) { value /= 1000; suffixIndex++; } const formattedValue = value.toFixed(1).replace(/\.0+$/, ''); return `${formattedValue} ${suffixes[suffixIndex]}`; } function gasFee(gas, price) { const near = yoctoToNear(Big(gas).mul(Big(price)).toString(), true); return `${near} Ⓝ`; } function currency(number) { let absNumber = Math.abs(number); const suffixes = ['', 'K', 'M', 'B', 'T', 'Q']; let suffixIndex = 0; while (absNumber >= 1000 && suffixIndex < suffixes.length - 1) { absNumber /= 1000; suffixIndex++; } let shortNumber = parseFloat(absNumber.toFixed(2)); return (number < 0 ? '-' : '') + shortNumber + ' ' + suffixes[suffixIndex]; } function formatDate(dateString) { const inputDate = new Date(dateString); const days = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ]; const months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; const dayOfWeek = days[inputDate.getDay()]; const month = months[inputDate.getMonth()]; const day = inputDate.getDate(); const year = inputDate.getFullYear(); const formattedDate = dayOfWeek + ', ' + month + ' ' + day + ', ' + year; return formattedDate; } function formatCustomDate(inputDate) { var date = new Date(inputDate); // Array of month names var monthNames = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ]; // Get month and day var month = monthNames[date.getMonth()]; var day = date.getDate(); // Create formatted date string in "MMM DD" format var formattedDate = month + ' ' + (day < 10 ? '0' + day : day); return formattedDate; } function shortenHex(address) { return `${address && address.substr(0, 6)}...${address.substr(-4)}`; } function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } function shortenToken(token) { return truncateString(token, 14, ''); } function shortenTokenSymbol(token) { return truncateString(token, 5, ''); } function gasPercentage(gasUsed, gasAttached) { if (!gasAttached) return 'N/A'; const formattedNumber = (Big(gasUsed).div(Big(gasAttached)) * 100).toFixed(2); return `${formattedNumber}%`; } function serialNumber(index, page, perPage) { return index + 1 + (page - 1) * perPage; } function capitalizeWords(str) { const words = str.split('_'); const capitalizedWords = words.map( (word) => word.charAt(0).toUpperCase() + word.slice(1), ); const result = capitalizedWords.join(' '); return result; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function getTimeAgoString(timestamp) { const currentUTC = Date.now(); const date = new Date(timestamp); const seconds = Math.floor((currentUTC - date.getTime()) / 1000); const intervals = { year: seconds / (60 * 60 * 24 * 365), month: seconds / (60 * 60 * 24 * 30), week: seconds / (60 * 60 * 24 * 7), day: seconds / (60 * 60 * 24), hour: seconds / (60 * 60), minute: seconds / 60, }; if (intervals.year == 1) { return Math.ceil(intervals.year) + ' year ago'; } else if (intervals.year > 1) { return Math.ceil(intervals.year) + ' years ago'; } else if (intervals.month > 1) { return Math.ceil(intervals.month) + ' months ago'; } else if (intervals.day > 1) { return Math.ceil(intervals.day) + ' days ago'; } else if (intervals.hour > 1) { return Math.ceil(intervals.hour) + ' hours ago'; } else if (intervals.minute > 1) { return Math.ceil(intervals.minute) + ' minutes ago'; } else { return 'a few seconds ago'; } } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function formatTimestampToString(timestamp) { const date = new Date(timestamp); // Format the date to 'YYYY-MM-DD HH:mm:ss' format const formattedDate = date.toISOString().replace('T', ' ').split('.')[0]; return formattedDate; } function convertToMetricPrefix(number) { const prefixes = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; // Metric prefixes let count = 0; while (Math.abs(number) >= 1000 && count < prefixes.length - 1) { number /= 1000; count++; } // Check if the number is close to an integer value if (Math.abs(number) >= 10) { number = Math.round(number); // Round the number to the nearest whole number return number + ' ' + prefixes[count]; } return ( Number(Math.floor(number * 100) / 100).toFixed(2) + ' ' + prefixes[count] ); } function formatNumber(value) { const suffixes = ['', 'K', 'M', 'B', 'T']; let suffixIndex = 0; while (value >= 10000 && suffixIndex < suffixes.length - 1) { value /= 1000; suffixIndex++; } const formattedValue = value.toFixed(1).replace(/\.0+$/, ''); return `${formattedValue} ${suffixes[suffixIndex]}`; } function gasFee(gas, price) { const near = yoctoToNear(Big(gas).mul(Big(price)).toString(), true); return `${near} Ⓝ`; } function currency(number) { let absNumber = Math.abs(number); const suffixes = ['', 'K', 'M', 'B', 'T', 'Q']; let suffixIndex = 0; while (absNumber >= 1000 && suffixIndex < suffixes.length - 1) { absNumber /= 1000; suffixIndex++; } let shortNumber = parseFloat(absNumber.toFixed(2)); return (number < 0 ? '-' : '') + shortNumber + ' ' + suffixes[suffixIndex]; } function formatDate(dateString) { const inputDate = new Date(dateString); const days = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ]; const months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; const dayOfWeek = days[inputDate.getDay()]; const month = months[inputDate.getMonth()]; const day = inputDate.getDate(); const year = inputDate.getFullYear(); const formattedDate = dayOfWeek + ', ' + month + ' ' + day + ', ' + year; return formattedDate; } function formatCustomDate(inputDate) { var date = new Date(inputDate); // Array of month names var monthNames = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ]; // Get month and day var month = monthNames[date.getMonth()]; var day = date.getDate(); // Create formatted date string in "MMM DD" format var formattedDate = month + ' ' + (day < 10 ? '0' + day : day); return formattedDate; } function shortenHex(address) { return `${address && address.substr(0, 6)}...${address.substr(-4)}`; } function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } function shortenToken(token) { return truncateString(token, 14, ''); } function shortenTokenSymbol(token) { return truncateString(token, 5, ''); } function gasPercentage(gasUsed, gasAttached) { if (!gasAttached) return 'N/A'; const formattedNumber = (Big(gasUsed).div(Big(gasAttached)) * 100).toFixed(2); return `${formattedNumber}%`; } function serialNumber(index, page, perPage) { return index + 1 + (page - 1) * perPage; } function capitalizeWords(str) { const words = str.split('_'); const capitalizedWords = words.map( (word) => word.charAt(0).toUpperCase() + word.slice(1), ); const result = capitalizedWords.join(' '); return result; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function formatTimestampToString(timestamp) { const date = new Date(timestamp); // Format the date to 'YYYY-MM-DD HH:mm:ss' format const formattedDate = date.toISOString().replace('T', ' ').split('.')[0]; return formattedDate; } function convertToMetricPrefix(number) { const prefixes = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; // Metric prefixes let count = 0; while (Math.abs(number) >= 1000 && count < prefixes.length - 1) { number /= 1000; count++; } // Check if the number is close to an integer value if (Math.abs(number) >= 10) { number = Math.round(number); // Round the number to the nearest whole number return number + ' ' + prefixes[count]; } return ( Number(Math.floor(number * 100) / 100).toFixed(2) + ' ' + prefixes[count] ); } function formatNumber(value) { const suffixes = ['', 'K', 'M', 'B', 'T']; let suffixIndex = 0; while (value >= 10000 && suffixIndex < suffixes.length - 1) { value /= 1000; suffixIndex++; } const formattedValue = value.toFixed(1).replace(/\.0+$/, ''); return `${formattedValue} ${suffixes[suffixIndex]}`; } function gasFee(gas, price) { const near = yoctoToNear(Big(gas).mul(Big(price)).toString(), true); return `${near} Ⓝ`; } function currency(number) { let absNumber = Math.abs(number); const suffixes = ['', 'K', 'M', 'B', 'T', 'Q']; let suffixIndex = 0; while (absNumber >= 1000 && suffixIndex < suffixes.length - 1) { absNumber /= 1000; suffixIndex++; } let shortNumber = parseFloat(absNumber.toFixed(2)); return (number < 0 ? '-' : '') + shortNumber + ' ' + suffixes[suffixIndex]; } function formatDate(dateString) { const inputDate = new Date(dateString); const days = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ]; const months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; const dayOfWeek = days[inputDate.getDay()]; const month = months[inputDate.getMonth()]; const day = inputDate.getDate(); const year = inputDate.getFullYear(); const formattedDate = dayOfWeek + ', ' + month + ' ' + day + ', ' + year; return formattedDate; } function formatCustomDate(inputDate) { var date = new Date(inputDate); // Array of month names var monthNames = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ]; // Get month and day var month = monthNames[date.getMonth()]; var day = date.getDate(); // Create formatted date string in "MMM DD" format var formattedDate = month + ' ' + (day < 10 ? '0' + day : day); return formattedDate; } function shortenHex(address) { return `${address && address.substr(0, 6)}...${address.substr(-4)}`; } function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } function shortenToken(token) { return truncateString(token, 14, ''); } function shortenTokenSymbol(token) { return truncateString(token, 5, ''); } function gasPercentage(gasUsed, gasAttached) { if (!gasAttached) return 'N/A'; const formattedNumber = (Big(gasUsed).div(Big(gasAttached)) * 100).toFixed(2); return `${formattedNumber}%`; } function serialNumber(index, page, perPage) { return index + 1 + (page - 1) * perPage; } function capitalizeWords(str) { const words = str.split('_'); const capitalizedWords = words.map( (word) => word.charAt(0).toUpperCase() + word.slice(1), ); const result = capitalizedWords.join(' '); return result; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } function shortenToken(token) { return truncateString(token, 14, ''); } function shortenTokenSymbol(token) { return truncateString(token, 5, ''); } function gasPercentage(gasUsed, gasAttached) { if (!gasAttached) return 'N/A'; const formattedNumber = (Big(gasUsed).div(Big(gasAttached)) * 100).toFixed(2); return `${formattedNumber}%`; } function serialNumber(index, page, perPage) { return index + 1 + (page - 1) * perPage; } function capitalizeWords(str) { const words = str.split('_'); const capitalizedWords = words.map( (word) => word.charAt(0).toUpperCase() + word.slice(1), ); const result = capitalizedWords.join(' '); return result; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } /* END_INCLUDE: "includes/formats.jsx" */ /* INCLUDE: "includes/near.jsx" */ function txnMethod( actions, t, ) { const count = actions?.length || 0; if (!count) return t ? t('txns:unknownType') : 'Unknown'; if (count > 1) return t ? t('txns:batchTxns') : 'Batch Transaction'; const action = actions[0]; if (action.action === 'FUNCTION_CALL') { return action.method; } return action.action; } function gasPrice(yacto) { const near = Big(yoctoToNear(yacto, false)).mul(Big(10).pow(12)).toString(); return `${localFormat(near)} Ⓝ / Tgas`; } function tokenAmount(amount, decimal, format) { if (amount === undefined || amount === null) return 'N/A'; const near = Big(amount).div(Big(10).pow(decimal)); const formattedValue = format ? near.toFixed(8).replace(/\.?0+$/, '') : near.toFixed(decimal).replace(/\.?0+$/, ''); return formattedValue; } function tokenPercentage( supply, amount, decimal, ) { const nearAmount = Big(amount).div(Big(10).pow(decimal)); const nearSupply = Big(supply); return nearAmount.div(nearSupply).mul(Big(100)).toFixed(2); } function price(amount, decimal, price) { const nearAmount = Big(amount).div(Big(10).pow(decimal)); return dollarFormat(nearAmount.mul(Big(price || 0)).toString()); } function mapRpcActionToAction(action) { if (action === 'CreateAccount') { return { action_kind: 'CreateAccount', args: {}, }; } if (typeof action === 'object') { const kind = Object.keys(action)[0]; return { action_kind: kind, args: action[kind], }; } return null; } function valueFromObj(obj) { const keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = obj[key]; if (typeof value === 'string') { return value; } if (typeof value === 'object') { const nestedValue = valueFromObj(value ); if (nestedValue) { return nestedValue; } } } return undefined; } function txnLogs(txn) { let txLogs = []; const outcomes = txn?.receipts_outcome || []; for (let i = 0; i < outcomes.length; i++) { const outcome = outcomes[i]; let logs = outcome?.outcome?.logs || []; if (logs.length > 0) { const mappedLogs = logs.map((log) => ({ contract: outcome?.outcome?.executor_id || '', logs: log, })); txLogs = [...txLogs, ...mappedLogs]; } } return txLogs; } function txnActions(txn) { const txActions = []; const receipts = txn?.receipts || []; for (let i = 0; i < receipts.length; i++) { const receipt = receipts[i]; const from = receipt?.predecessor_id; const to = receipt?.receiver_id; if (Array.isArray(receipt?.receipt)) { const actions = receipt.receipt; for (let j = 0; j < actions.length; j++) { const action = actions[j]; txActions.push({ from, to, ...action }); } } else { const actions = receipt?.receipt?.Action?.actions || []; for (let j = 0; j < actions.length; j++) { const action = mapRpcActionToAction(actions[j]); txActions.push({ from, to, ...action }); } } } return txActions.filter( (action) => action.action_kind !== 'FunctionCall' && action.from !== 'system', ); } function txnErrorMessage(txn) { const kind = txn?.status?.Failure?.ActionError?.kind; if (typeof kind === 'string') return kind; if (typeof kind === 'object') { return valueFromObj(kind); } return null; } function formatLine(line, offset, format) { let result = `${offset.toString(16).padStart(8, '0')} `; const bytes = line.split(' ').filter(Boolean); bytes.forEach((byte, index) => { if (index > 0 && index % 4 === 0) { result += ' '; } result += byte.toUpperCase().padEnd(2, ' ') + ' '; }); if (format === 'default') { result += ` ${String.fromCharCode( ...bytes.map((b) => parseInt(b, 16)), )}`; } return result.trimEnd(); } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function dollarFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formattedNumber; } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } /* END_INCLUDE: "includes/near.jsx" */ /* INCLUDE: "includes/libs.jsx" */ function getConfig(network) { switch (network) { case 'mainnet': return { ownerId: 'nearblocks.near', nodeUrl: 'https://rpc.mainnet.near.org', backendUrl: 'https://api3.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://nearblocks.io/', }; case 'testnet': return { ownerId: 'nearblocks.testnet', nodeUrl: 'https://rpc.testnet.near.org', backendUrl: 'https://api3-testnet.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://testnet.nearblocks.io/', }; default: return {}; } } function debounce( delay, func, ) { let timer; let active = true; const debounced = (arg) => { if (active) { clearTimeout(timer); timer = setTimeout(() => { active && func(arg); timer = undefined; }, delay); } else { func(arg); } }; debounced.isPending = () => { return timer !== undefined; }; debounced.cancel = () => { active = false; }; debounced.flush = (arg) => func(arg); return debounced; } function timeAgo(unixTimestamp) { const currentTimestamp = Math.floor(Date.now() / 1000); const secondsAgo = currentTimestamp - unixTimestamp; if (secondsAgo < 5) { return 'Just now'; } else if (secondsAgo < 60) { return `${secondsAgo} seconds ago`; } else if (secondsAgo < 3600) { const minutesAgo = Math.floor(secondsAgo / 60); return `${minutesAgo} minute${minutesAgo > 1 ? 's' : ''} ago`; } else if (secondsAgo < 86400) { const hoursAgo = Math.floor(secondsAgo / 3600); return `${hoursAgo} hour${hoursAgo > 1 ? 's' : ''} ago`; } else { const daysAgo = Math.floor(secondsAgo / 86400); return `${daysAgo} day${daysAgo > 1 ? 's' : ''} ago`; } } function shortenAddress(address) { const string = String(address); if (string.length <= 20) return string; return `${string.substr(0, 10)}...${string.substr(-7)}`; } function urlHostName(url) { try { const domain = new URL(url); return domain?.hostname ?? null; } catch (e) { return null; } } function holderPercentage(supply, quantity) { return Math.min(Big(quantity).div(Big(supply)).mul(Big(100)).toFixed(2), 100); } function isAction(type) { const actions = [ 'DEPLOY_CONTRACT', 'TRANSFER', 'STAKE', 'ADD_KEY', 'DELETE_KEY', 'DELETE_ACCOUNT', ]; return actions.includes(type.toUpperCase()); } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function nanoToMilli(nano) { return new Big(nano).div(new Big(10).pow(6)).round().toNumber(); } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function getConfig(network) { switch (network) { case 'mainnet': return { ownerId: 'nearblocks.near', nodeUrl: 'https://rpc.mainnet.near.org', backendUrl: 'https://api3.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://nearblocks.io/', }; case 'testnet': return { ownerId: 'nearblocks.testnet', nodeUrl: 'https://rpc.testnet.near.org', backendUrl: 'https://api3-testnet.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://testnet.nearblocks.io/', }; default: return {}; } } function debounce( delay, func, ) { let timer; let active = true; const debounced = (arg) => { if (active) { clearTimeout(timer); timer = setTimeout(() => { active && func(arg); timer = undefined; }, delay); } else { func(arg); } }; debounced.isPending = () => { return timer !== undefined; }; debounced.cancel = () => { active = false; }; debounced.flush = (arg) => func(arg); return debounced; } function timeAgo(unixTimestamp) { const currentTimestamp = Math.floor(Date.now() / 1000); const secondsAgo = currentTimestamp - unixTimestamp; if (secondsAgo < 5) { return 'Just now'; } else if (secondsAgo < 60) { return `${secondsAgo} seconds ago`; } else if (secondsAgo < 3600) { const minutesAgo = Math.floor(secondsAgo / 60); return `${minutesAgo} minute${minutesAgo > 1 ? 's' : ''} ago`; } else if (secondsAgo < 86400) { const hoursAgo = Math.floor(secondsAgo / 3600); return `${hoursAgo} hour${hoursAgo > 1 ? 's' : ''} ago`; } else { const daysAgo = Math.floor(secondsAgo / 86400); return `${daysAgo} day${daysAgo > 1 ? 's' : ''} ago`; } } function shortenAddress(address) { const string = String(address); if (string.length <= 20) return string; return `${string.substr(0, 10)}...${string.substr(-7)}`; } function urlHostName(url) { try { const domain = new URL(url); return domain?.hostname ?? null; } catch (e) { return null; } } function holderPercentage(supply, quantity) { return Math.min(Big(quantity).div(Big(supply)).mul(Big(100)).toFixed(2), 100); } function isAction(type) { const actions = [ 'DEPLOY_CONTRACT', 'TRANSFER', 'STAKE', 'ADD_KEY', 'DELETE_KEY', 'DELETE_ACCOUNT', ]; return actions.includes(type.toUpperCase()); } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function getConfig(network) { switch (network) { case 'mainnet': return { ownerId: 'nearblocks.near', nodeUrl: 'https://rpc.mainnet.near.org', backendUrl: 'https://api3.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://nearblocks.io/', }; case 'testnet': return { ownerId: 'nearblocks.testnet', nodeUrl: 'https://rpc.testnet.near.org', backendUrl: 'https://api3-testnet.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://testnet.nearblocks.io/', }; default: return {}; } } function debounce( delay, func, ) { let timer; let active = true; const debounced = (arg) => { if (active) { clearTimeout(timer); timer = setTimeout(() => { active && func(arg); timer = undefined; }, delay); } else { func(arg); } }; debounced.isPending = () => { return timer !== undefined; }; debounced.cancel = () => { active = false; }; debounced.flush = (arg) => func(arg); return debounced; } function timeAgo(unixTimestamp) { const currentTimestamp = Math.floor(Date.now() / 1000); const secondsAgo = currentTimestamp - unixTimestamp; if (secondsAgo < 5) { return 'Just now'; } else if (secondsAgo < 60) { return `${secondsAgo} seconds ago`; } else if (secondsAgo < 3600) { const minutesAgo = Math.floor(secondsAgo / 60); return `${minutesAgo} minute${minutesAgo > 1 ? 's' : ''} ago`; } else if (secondsAgo < 86400) { const hoursAgo = Math.floor(secondsAgo / 3600); return `${hoursAgo} hour${hoursAgo > 1 ? 's' : ''} ago`; } else { const daysAgo = Math.floor(secondsAgo / 86400); return `${daysAgo} day${daysAgo > 1 ? 's' : ''} ago`; } } function shortenAddress(address) { const string = String(address); if (string.length <= 20) return string; return `${string.substr(0, 10)}...${string.substr(-7)}`; } function urlHostName(url) { try { const domain = new URL(url); return domain?.hostname ?? null; } catch (e) { return null; } } function holderPercentage(supply, quantity) { return Math.min(Big(quantity).div(Big(supply)).mul(Big(100)).toFixed(2), 100); } function isAction(type) { const actions = [ 'DEPLOY_CONTRACT', 'TRANSFER', 'STAKE', 'ADD_KEY', 'DELETE_KEY', 'DELETE_ACCOUNT', ]; return actions.includes(type.toUpperCase()); } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function fiatValue(big, price) { const value = Big(big).mul(Big(price)).toString(); const formattedNumber = Number(value).toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 6, }); return formattedNumber; } function nanoToMilli(nano) { return new Big(nano).div(new Big(10).pow(6)).round().toNumber(); } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function getConfig(network) { switch (network) { case 'mainnet': return { ownerId: 'nearblocks.near', nodeUrl: 'https://rpc.mainnet.near.org', backendUrl: 'https://api3.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://nearblocks.io/', }; case 'testnet': return { ownerId: 'nearblocks.testnet', nodeUrl: 'https://rpc.testnet.near.org', backendUrl: 'https://api3-testnet.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://testnet.nearblocks.io/', }; default: return {}; } } function debounce( delay, func, ) { let timer; let active = true; const debounced = (arg) => { if (active) { clearTimeout(timer); timer = setTimeout(() => { active && func(arg); timer = undefined; }, delay); } else { func(arg); } }; debounced.isPending = () => { return timer !== undefined; }; debounced.cancel = () => { active = false; }; debounced.flush = (arg) => func(arg); return debounced; } function timeAgo(unixTimestamp) { const currentTimestamp = Math.floor(Date.now() / 1000); const secondsAgo = currentTimestamp - unixTimestamp; if (secondsAgo < 5) { return 'Just now'; } else if (secondsAgo < 60) { return `${secondsAgo} seconds ago`; } else if (secondsAgo < 3600) { const minutesAgo = Math.floor(secondsAgo / 60); return `${minutesAgo} minute${minutesAgo > 1 ? 's' : ''} ago`; } else if (secondsAgo < 86400) { const hoursAgo = Math.floor(secondsAgo / 3600); return `${hoursAgo} hour${hoursAgo > 1 ? 's' : ''} ago`; } else { const daysAgo = Math.floor(secondsAgo / 86400); return `${daysAgo} day${daysAgo > 1 ? 's' : ''} ago`; } } function shortenAddress(address) { const string = String(address); if (string.length <= 20) return string; return `${string.substr(0, 10)}...${string.substr(-7)}`; } function urlHostName(url) { try { const domain = new URL(url); return domain?.hostname ?? null; } catch (e) { return null; } } function holderPercentage(supply, quantity) { return Math.min(Big(quantity).div(Big(supply)).mul(Big(100)).toFixed(2), 100); } function isAction(type) { const actions = [ 'DEPLOY_CONTRACT', 'TRANSFER', 'STAKE', 'ADD_KEY', 'DELETE_KEY', 'DELETE_ACCOUNT', ]; return actions.includes(type.toUpperCase()); } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function localFormat(number) { const formattedNumber = Number(number).toLocaleString('en', { minimumFractionDigits: 0, maximumFractionDigits: 5, }); return formattedNumber; } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } /* END_INCLUDE: "includes/libs.jsx" */ /* INCLUDE COMPONENT: "includes/icons/FaLongArrowAltRight.jsx" */ const FaLongArrowAltRight = () => { return ( <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512"> <path d="M313.941 216H12c-6.627 0-12 5.373-12 12v56c0 6.627 5.373 12 12 12h301.941v46.059c0 21.382 25.851 32.09 40.971 16.971l86.059-86.059c9.373-9.373 9.373-24.569 0-33.941l-86.059-86.059c-15.119-15.119-40.971-4.411-40.971 16.971V216" fill="#ffffff" /> </svg> ); };/* END_INCLUDE COMPONENT: "includes/icons/FaLongArrowAltRight.jsx" */ /* INCLUDE COMPONENT: "includes/Common/Status.jsx" */ const FaCheckCircle = () => { return ( <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"> <path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z" fill="#50C878" /> </svg> ); }; const FaTimesCircle = () => { return ( <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"> <path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z" fill="#ff0000" /> </svg> ); }; const FaHourglassStart = () => { return ( <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 384 512"> <path d="M32 0C14.3 0 0 14.3 0 32S14.3 64 32 64V75c0 42.4 16.9 83.1 46.9 113.1L146.7 256 78.9 323.9C48.9 353.9 32 394.6 32 437v11c-17.7 0-32 14.3-32 32s14.3 32 32 32H64 320h32c17.7 0 32-14.3 32-32s-14.3-32-32-32V437c0-42.4-16.9-83.1-46.9-113.1L237.3 256l67.9-67.9c30-30 46.9-70.7 46.9-113.1V64c17.7 0 32-14.3 32-32s-14.3-32-32-32H320 64 32zM288 437v11H96V437c0-25.5 10.1-49.9 28.1-67.9L192 301.3l67.9 67.9c18 18 28.1 42.4 28.1 67.9z" fill="#FFEB3B" /> </svg> ); }; const getOptions = (status) => { switch (status) { case null: return { bg: 'bg-yellow-50', text: 'text-yellow-500', icon: FaHourglassStart, label: 'Pending', }; case false: return { bg: 'bg-red-50', text: 'text-red-500', icon: FaTimesCircle, label: 'Failure', }; default: return { bg: 'bg-emerald-50', text: 'text-emerald-500', icon: FaCheckCircle, label: 'Success', }; } }; const TxnStatus = (props) => { const option = getOptions(props.status); const Icon = option.icon; return ( <div className="w-full md:w-3/4 break-words"> <span className={`inline-flex items-center text-xs rounded py-1 ${ option.bg } ${option.text} ${props.showLabel ? ' px-2' : ' px-1'}`} > <Icon /> {props.showLabel && <span className="ml-2">{option.label}</span>} </span> </div> ); };/* END_INCLUDE COMPONENT: "includes/Common/Status.jsx" */ /* INCLUDE COMPONENT: "includes/Common/Filter.jsx" */ const Filter = (props) => { return ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={16} height={16} {...props} > <path fill="none" d="M0 0h24v24H0z" /> <path d="M14 14v6l-4 2v-8L4 5V3h16v2l-6 9zM6.404 5L12 13.394 17.596 5H6.404z" /> </svg> ); };/* END_INCLUDE COMPONENT: "includes/Common/Filter.jsx" */ /* INCLUDE COMPONENT: "includes/icons/SortIcon.jsx" */ const ArrowUp = (props) => { return ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={24} height={24} {...props} > <path fill="none" d="M0 0h24v24H0z" /> <path d="M12 10.828l-4.95 4.95-1.414-1.414L12 8l6.364 6.364-1.414 1.414z" /> </svg> ); }; const SortIcon = (props) => { return ( <ArrowUp className={`h-3 w-3 fill-current transition-transform mr-1 duration-700 ${ props.order !== 'asc' ? 'transform rotate-180' : 'transform rotate-0' }`} /> ); };/* END_INCLUDE COMPONENT: "includes/icons/SortIcon.jsx" */ /* INCLUDE COMPONENT: "includes/icons/CloseCircle.jsx" */ const CloseCircle = (props) => { const handleClick = () => { if (props.onClick) { props.onClick('All'); } }; return ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={24} height={24} className={props.className} onClick={handleClick} > <path fill="none" d="M0 0h24v24H0z" /> <path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 100-16 8 8 0 000 16zm0-9.414l2.828-2.829 1.415 1.415L13.414 12l2.829 2.828-1.415 1.415L12 13.414l-2.828 2.829-1.415-1.415L10.586 12 7.757 9.172l1.415-1.415L12 10.586z" /> </svg> ); };/* END_INCLUDE COMPONENT: "includes/icons/CloseCircle.jsx" */ /* INCLUDE COMPONENT: "includes/Common/Skeleton.jsx" */ /** * @interface Props * @param {string} [className] - The CSS class name(s) for styling purposes. */ const Skeleton = (props) => { return ( <div className={`bg-gray-200 rounded shadow-sm animate-pulse ${props.className}`} ></div> ); };/* END_INCLUDE COMPONENT: "includes/Common/Skeleton.jsx" */ /* INCLUDE COMPONENT: "includes/icons/Clock.jsx" */ /** * @interface Props * @param {string} [className] - The CSS class name(s) for styling purposes. */ const Clock = (props) => ( <svg viewBox="64 64 896 896" focusable="false" data-icon="clock-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" {...props} > <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> <path d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"></path> </svg> );/* END_INCLUDE COMPONENT: "includes/icons/Clock.jsx" */ function MainComponent(props) { const { network, currentPage, filters, setPage, handleFilter, onFilterClear, t, } = props; const [isLoading, setIsLoading] = useState(false); const [totalCount, setTotalCount] = useState(0); const [txns, setTxns] = useState({}); const [showAge, setShowAge] = useState(true); const [sorting, setSorting] = useState('desc'); const errorMessage = t ? t('txns:noTxns') : ' No transactions found!'; const config = getConfig(network); const toggleShowAge = () => setShowAge((s) => !s); useEffect(() => { function fetchTotalTxns(qs) { const queryParams = qs ? '?' + qs : ''; asyncFetch(`${config?.backendUrl}txns/count${queryParams}`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }) .then( (data ) => { const resp = data?.body?.txns?.[0]; if (data.status === 200) { setTotalCount(resp?.count); } }, ) .catch(() => {}) .finally(() => {}); } function fetchTxnsData(qs, sqs, page) { setIsLoading(true); const queryParams = qs ? qs + '&' : ''; asyncFetch( `${config?.backendUrl}txns?${queryParams}order=${sqs}&page=${page}&per_page=25`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }, ) .then((data) => { const resp = data?.body?.txns; if (data.status === 200) { if (Array.isArray(resp) && resp.length > 0) { setTxns((prevData) => ({ ...prevData, [page]: resp || [] })); } } }) .catch(() => {}) .finally(() => { setIsLoading(false); }); } let urlString = ''; if (filters && Object.keys(filters).length > 0) { urlString = Object.keys(filters) .map( (key) => `${encodeURIComponent(key)}=${encodeURIComponent(filters[key])}`, ) .join('&'); } if (urlString && sorting) { fetchTotalTxns(urlString); fetchTxnsData(urlString, sorting, currentPage); } else if (sorting && (!filters || Object.keys(filters).length === 0)) { fetchTotalTxns(); fetchTxnsData('', sorting, currentPage); } }, [config?.backendUrl, currentPage, filters, sorting]); let filterValue; const onInputChange = (event) => { filterValue = event.target.value; // Do something with the value if needed }; const onFilter = ( e, name, ) => { e.preventDefault(); handleFilter(name, filterValue); }; const onClear = (name) => { if (onFilterClear && filters) { onFilterClear(name); } }; const onOrder = () => { setSorting((state) => (state === 'asc' ? 'desc' : 'asc')); }; const columns = [ { header: <span></span>, key: '', cell: (row) => ( <> <TxnStatus status={row.outcomes.status} showLabel={false} /> </> ), tdClassName: 'pl-5 pr-2 py-4 whitespace-nowrap text-sm text-nearblue-600 flex justify-end ', }, { header: <span>{t ? t('txns:hash') : 'TXN HASH'}</span>, key: 'transaction_hash', cell: (row) => ( <span> <Tooltip.Provider> <Tooltip.Root> <Tooltip.Trigger asChild> <span className="truncate max-w-[120px] inline-block align-bottom text-green-500"> <a href={`/txns/${row.transaction_hash}`} className="hover:no-underline" > <a className="text-green-500 font-medium hover:no-underline"> {row.transaction_hash} </a> </a> </span> </Tooltip.Trigger> <Tooltip.Content className="h-auto max-w-xs bg-black bg-opacity-90 z-10 text-xs text-white p-2 break-words" align="start" side="bottom" > {row.transaction_hash} </Tooltip.Content> </Tooltip.Root> </Tooltip.Provider> </span> ), tdClassName: 'px-5 py-4 whitespace-nowrap text-sm text-nearblue-600', thClassName: 'px-5 py-4 text-left whitespace-nowrap text-xs font-semibold text-nearblue-600 uppercase tracking-wider', }, { header: ( <Popover.Root> <Popover.Trigger asChild className="flex items-center px-6 py-4 text-left text-xs font-semibold text-nearblue-600 uppercase tracking-wider focus:outline-none" > <button className="IconButton" aria-label="Update dimensions"> {t ? t('txns:type') : 'METHOD'} <Filter className="h-4 w-4 fill-current ml-2" /> </button> </Popover.Trigger> <Popover.Content className="bg-white shadow-lg border rounded-b-lg p-2" sideOffset={5} > <div className="flex flex-col"> <input name="type" value={filters ? filters?.method : ''} onChange={onInputChange} placeholder="Search by method" className="border rounded h-8 mb-2 px-2 text-nearblue-600 text-xs" /> <div className="flex"> <button type="submit" onClick={(e) => onFilter(e, 'method')} className="flex items-center justify-center flex-1 rounded bg-green-500 h-7 text-white text-xs mr-2" > <Filter className="h-3 w-3 fill-current mr-2" />{' '} {t ? t('txns:filter.filter') : 'Filter'} </button> <button name="type" type="button" onClick={() => onClear('method')} className="flex-1 rounded bg-gray-300 text-xs h-7" > {t ? t('txns:filter.clear') : 'Clear'} </button> </div> </div> </Popover.Content> </Popover.Root> ), key: 'actions', cell: (row) => ( <span> <Tooltip.Provider> <Tooltip.Root> <Tooltip.Trigger asChild> <span className="bg-blue-900/10 text-xs text-nearblue-600 rounded-xl px-2 py-1 max-w-[120px] inline-flex truncate"> <span className="block truncate"> {txnMethod(row.actions, t)} </span> </span> </Tooltip.Trigger> <Tooltip.Content className="h-auto max-w-xs bg-black bg-opacity-90 z-10 text-xs text-white px-3 py-2 break-words" align="center" side="bottom" > {txnMethod(row.actions, t)} </Tooltip.Content> </Tooltip.Root> </Tooltip.Provider> </span> ), tdClassName: 'px-5 py-4 whitespace-nowrap text-sm text-nearblue-600', }, { header: <span>{t ? t('txns:depositValue') : 'DEPOSIT VALUE'}</span>, key: 'deposit', cell: (row) => ( <span>{yoctoToNear(row.actions_agg?.deposit || 0, true)} Ⓝ</span> ), tdClassName: 'px-5 py-4 whitespace-nowrap text-sm text-nearblue-600', thClassName: 'px-5 py-4 text-left text-xs font-semibold text-nearblue-600 uppercase tracking-wider whitespace-nowrap', }, { header: <span>{t ? t('txns:txnFee') : 'TXN FEE'}</span>, key: 'transaction_fee', cell: (row) => ( <span> {' '} {yoctoToNear(row.outcomes_agg?.transaction_fee || 0, true)} Ⓝ </span> ), tdClassName: 'px-6 py-4 whitespace-nowrap text-sm text-nearblue-600', thClassName: 'px-5 py-4 text-left whitespace-nowrap text-xs font-semibold text-nearblue-600 uppercase tracking-wider', }, { header: ( <Popover.Root> <Popover.Trigger asChild className="flex items-center px-6 py-4 text-left text-xs font-semibold text-nearblue-600 uppercase tracking-wider focus:outline-none" > <button className="IconButton" aria-label="Update dimensions"> {t ? t('txns:from') : 'FROM'} <Filter className="h-4 w-4 fill-current ml-2" /> </button> </Popover.Trigger> <Popover.Content className="bg-white shadow-lg border rounded-b-lg p-2" sideOffset={5} > <input name="from" value={filters ? filters?.from : ''} onChange={onInputChange} placeholder={ t ? t('txns:filter.placeholder') : 'Search by address e.g. Ⓝ..' } className="border rounded h-8 mb-2 px-2 text-nearblue-600 text-xs" /> <div className="flex"> <button type="submit" onClick={(e) => onFilter(e, 'from')} className="flex items-center justify-center flex-1 rounded bg-green-500 h-7 text-white text-xs mr-2" > <Filter className="h-3 w-3 fill-current mr-2" />{' '} {t ? t('txns:filter.filter') : 'Filter'} </button> <button name="from" type="button" onClick={() => onClear('from')} className="flex-1 rounded bg-gray-300 text-xs h-7" > {t ? t('txns:filter.clear') : 'Clear'} </button> </div> </Popover.Content> </Popover.Root> ), key: 'signer_account_id', cell: (row) => ( <span> <Tooltip.Provider> <Tooltip.Root> <Tooltip.Trigger asChild> <span className="truncate max-w-[120px] inline-block align-bottom text-green-500"> <a href={`/address/${row.signer_account_id}`} className="hover:no-underline" > <a className="text-green-500 hover:no-underline"> {row.signer_account_id} </a> </a> </span> </Tooltip.Trigger> <Tooltip.Content className="h-auto max-w-xs bg-black bg-opacity-90 z-10 text-xs text-white px-3 py-2 break-words" align="start" side="bottom" > {truncateString(row.signer_account_id, 18, '...')} </Tooltip.Content> </Tooltip.Root> </Tooltip.Provider> </span> ), tdClassName: 'px-5 py-4 whitespace-nowrap text-sm text-nearblue-600 font-medium', }, { header: <span></span>, key: '', cell: () => ( <div className="w-5 h-5 p-1 bg-green-100 rounded-full text-center flex justify-center items-center mx-auto text-white"> <FaLongArrowAltRight /> </div> ), }, { header: ( <Popover.Root> <Popover.Trigger asChild className="flex items-center px-6 py-4 text-left text-xs font-semibold text-nearblue-600 uppercase tracking-wider focus:outline-none" > <button className="IconButton" aria-label="Update dimensions"> {t ? t('txns:to') : 'To'} <Filter className="h-4 w-4 fill-current ml-2" /> </button> </Popover.Trigger> <Popover.Content className="bg-white shadow-lg border rounded-b-lg p-2" sideOffset={5} > <input name="to" value={filters ? filters?.to : ''} onChange={onInputChange} placeholder={ t ? t('txns:filter.placeholder') : 'Search by address e.g. Ⓝ..' } className="border rounded h-8 mb-2 px-2 text-nearblue-600 text-xs" /> <div className="flex"> <button type="submit" onClick={(e) => onFilter(e, 'to')} className="flex items-center justify-center flex-1 rounded bg-green-500 h-7 text-white text-xs mr-2" > <Filter className="h-3 w-3 fill-current mr-2" />{' '} {t ? t('txns:filter.filter') : 'Filter'} </button> <button name="to" type="button" onClick={() => onClear('to')} className="flex-1 rounded bg-gray-300 text-xs h-7" > {t ? t('txns:filter.clear') : 'Clear'} </button> </div> </Popover.Content> </Popover.Root> ), key: 'receiver_account_id', cell: (row) => ( <span> <Tooltip.Provider> <Tooltip.Root> <Tooltip.Trigger asChild> <span> <a href={`/address/${row.receiver_account_id}`} className="hover:no-underline" > <a className="text-green-500 hover:no-underline"> {truncateString(row.receiver_account_id, 17, '...')} </a> </a> </span> </Tooltip.Trigger> <Tooltip.Content className="h-auto max-w-xs bg-black bg-opacity-90 z-10 text-xs text-white px-3 py-2 break-words" align="start" side="bottom" > {row.receiver_account_id} </Tooltip.Content> </Tooltip.Root> </Tooltip.Provider> </span> ), tdClassName: 'px-5 py-4 whitespace-nowrap text-sm text-nearblue-600 font-medium', }, { header: <span>{t ? t('txns:blockHeight') : ' BLOCK HEIGHT'}</span>, key: 'block_height', cell: (row) => ( <span> <a href={`/blocks/${row.included_in_block_hash}`} className="hover:no-underline" > <a className="text-green-500 hover:no-underline"> {localFormat(row.block?.block_height)} </a> </a> </span> ), tdClassName: 'px-5 py-4 whitespace-nowrap text-sm text-nearblue-600 font-medium', thClassName: 'px-5 py-4 text-left text-xs font-semibold text-nearblue-600 uppercase tracking-wider whitespace-nowrap', }, { header: ( <div className="w-full inline-flex px-5 py-4"> <Tooltip.Provider> <Tooltip.Root> <Tooltip.Trigger asChild> <button type="button" onClick={toggleShowAge} className="text-left text-xs w-full flex items-center font-semibold uppercase tracking-wider text-green-500 focus:outline-none whitespace-nowrap" > {showAge ? t ? t('txns:age') : 'AGE' : t ? t('txns:ageDT') : 'DATE TIME (UTC)'} {showAge && <Clock className="text-green-500 ml-2" />} </button> </Tooltip.Trigger> <Tooltip.Content className="h-auto max-w-xs bg-black bg-opacity-90 z-10 text-xs text-white px-3 py-2 break-words" align="center" side="top" > {showAge ? 'Click to show Datetime Format' : 'Click to show Age Format'} </Tooltip.Content> </Tooltip.Root> </Tooltip.Provider> <button type="button" onClick={onOrder} className="px-2"> <div className="text-nearblue-600 font-semibold"> <SortIcon order={sorting} /> </div> </button> </div> ), key: 'block_timestamp', cell: (row) => ( <span> <Tooltip.Provider> <Tooltip.Root> <Tooltip.Trigger asChild> <span> {!showAge ? formatTimestampToString(nanoToMilli(row.block_timestamp)) : getTimeAgoString(nanoToMilli(row.block_timestamp))} </span> </Tooltip.Trigger> <Tooltip.Content className="h-auto max-w-xs bg-black bg-opacity-90 z-10 text-xs text-white px-3 py-2 break-words" align="start" side="bottom" > {showAge ? formatTimestampToString(nanoToMilli(row.block_timestamp)) : getTimeAgoString(nanoToMilli(row.block_timestamp))} </Tooltip.Content> </Tooltip.Root> </Tooltip.Provider> </span> ), tdClassName: 'px-5 py-4 whitespace-nowrap text-sm text-nearblue-600', thClassName: 'inline-flex whitespace-nowrap', }, ]; return ( <div className=" bg-white border soft-shadow rounded-xl overflow-hidden"> {isLoading ? ( <div className="pl-6 max-w-lg w-full py-5 "> <Skeleton className="h-4" /> </div> ) : ( <div className={`flex flex-col lg:flex-row pt-4`}> <div className="flex flex-col"> <p className="leading-7 pl-6 text-sm mb-4 text-nearblue-600"> {t ? t('txns:listing', { count: localFormat(totalCount), }) : `More than > ${totalCount} transactions found`} </p> </div> {filters && Object.keys(filters).length > 0 && ( <div className="flex items-center px-2 text-sm mb-4 text-nearblue-600 lg:ml-auto"> Filtered By: <span className="flex items-center bg-gray-100 rounded-full px-3 py-1 ml-1 space-x-2"> {filters && Object.keys(filters).map((key) => ( <span className="flex" key={key}> {capitalizeFirstLetter(key)}:{' '} <span className="inline-block truncate max-w-[120px]"> <span className="font-semibold">{filters[key]}</span> </span> </span> ))} <CloseCircle className="w-4 h-4 fill-current cursor-pointer" onClick={onClear} /> </span> </div> )} </div> )} { <Widget src={`${config.ownerId}/widget/bos-components.components.Shared.Table`} props={{ columns: columns, data: txns[currentPage], isLoading: isLoading, isPagination: true, count: totalCount, page: currentPage, limit: 25, pageLimit: 200, setPage: setPage, Error: errorMessage, }} /> } </div> ); } return MainComponent(props, context);