diff --git a/src/context/APIContext.tsx b/src/context/APIContext.tsx index 654e4f2e..77022323 100644 --- a/src/context/APIContext.tsx +++ b/src/context/APIContext.tsx @@ -124,6 +124,7 @@ const APIContextProvider = ({ children }: any) => { const [analyticsData1, setAnalyticsData1] = useState() const [analyticsData2, setAnalyticsData2] = useState() const [analyticsData3, setAnalyticsData3] = useState() + const [analyticsData4, setAnalyticsData4] = useState() const [L5AnalyticsApiData, setL5AnalyticsApiData] = useState() const [volume, setVolume] = useState(Number) const [marketCap, setMarketCap] = useState(Number) @@ -224,6 +225,15 @@ const APIContextProvider = ({ children }: any) => { setAnalyticsData3(response) }) + const API_DATA_SECRET_4 = `https://dashboardstats.secretsaturn.net/source/relayer_stats/data.json` + fetch(API_DATA_SECRET_4) + .catch((error: any) => console.error(error)) + .then((response) => (response as any).json()) + .catch((error: any) => console.error(error)) + .then((response) => { + setAnalyticsData4(response) + }) + const LAVENDERFIVE_API_URL_SECRET_STATUS = `https://api.lavenderfive.com/networks/secretnetwork` fetch(LAVENDERFIVE_API_URL_SECRET_STATUS) .catch((error: any) => console.error(error)) @@ -385,6 +395,7 @@ const APIContextProvider = ({ children }: any) => { analyticsData1, analyticsData2, analyticsData3, + analyticsData4, L5AnalyticsApiData, bondedToken, notBondedToken, diff --git a/src/pages/analytics/Analytics.tsx b/src/pages/analytics/Analytics.tsx index a8ce7a02..dfe34091 100644 --- a/src/pages/analytics/Analytics.tsx +++ b/src/pages/analytics/Analytics.tsx @@ -7,9 +7,12 @@ import AccountsChart from './components/AccountsChart' import ValidatorsChart from './components/ValidatorsChart' import ContractsChart from './components/ContractsChart' import TransactionsChart from './components/TransactionsChart' +import RelayerChartWithDateSlider from './components/RelayerChartWithDateSlider' +import RelayerChartWithChainSlider from './components/RelayerChartWithChainSlider' +import RelayerChartWithProviderSlider from './components/RelayerChartWithProviderSlider' function Analytics() { - const { L5AnalyticsApiData, analyticsData1, analyticsData2, analyticsData3 } = useContext(APIContext) + const { L5AnalyticsApiData, analyticsData1, analyticsData2, analyticsData3, analyticsData4 } = useContext(APIContext) useEffect(() => { trackMixPanelEvent('Open Analytics Tab') @@ -61,6 +64,24 @@ function Analytics() { ) : null} + {analyticsData4 ? ( + <> +
+
+
+ +
+
+ +
+
+ +
+
+
+ + ) : null} + {L5AnalyticsApiData ? (
diff --git a/src/pages/analytics/components/RelayerChartWithChainSlider.tsx b/src/pages/analytics/components/RelayerChartWithChainSlider.tsx new file mode 100644 index 00000000..eda37b0e --- /dev/null +++ b/src/pages/analytics/components/RelayerChartWithChainSlider.tsx @@ -0,0 +1,270 @@ +import { useContext, useEffect, useState } from 'react' +import { bech32PrefixToChainName, formatNumber } from 'utils/commons' +import Tooltip from '@mui/material/Tooltip' +import Slider from '@mui/material/Slider' +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + BarElement, + Tooltip as ChartTooltip, + Legend, + BarController +} from 'chart.js' +import { Bar } from 'react-chartjs-2' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faInfoCircle } from '@fortawesome/free-solid-svg-icons' +import { useUserPreferencesStore } from 'store/UserPreferences' +import { APIContext } from 'context/APIContext' + +ChartJS.register(CategoryScale, LinearScale, BarElement, ChartTooltip, Legend, BarController) + +type Entry = { + Date: string + IBC_Counterpart: string + Relayer: string + Transactions: number +} + +export default function RelayerChartWithChainSlider() { + const { theme } = useUserPreferencesStore() + const { analyticsData4 } = useContext(APIContext) + const [chartData, setChartData] = useState(null) + const [chainLabels, setChainLabels] = useState([]) + const [selectedChainIndex, setSelectedChainIndex] = useState(0) + const [chainMap, setChainMap] = useState>({}) + const [marks, setMarks] = useState<{ value: number; label: string }[]>([]) + + const updateChartDataForChain = (chainName: string, entries: Entry[]) => { + // Initialize data structures + const dataMatrix: Map> = new Map() + const datesSet: Set = new Set() + const relayersSet: Set = new Set() + + // Build dataMatrix and collect unique dates and relayers + for (const entry of entries) { + const date = new Date(entry.Date).toISOString().split('T')[0] + const relayer = entry.Relayer || 'Other' + + datesSet.add(date) + relayersSet.add(relayer) + + if (!dataMatrix.has(relayer)) { + dataMatrix.set(relayer, new Map()) + } + const dateMap = dataMatrix.get(relayer)! + dateMap.set(date, (dateMap.get(date) || 0) + entry.Transactions) + } + + // Sort dates + const sortedDates = Array.from(datesSet).sort((a, b) => new Date(a).getTime() - new Date(b).getTime()) + + // Sort relayers + const sortedRelayers = Array.from(relayersSet).sort((a, b) => a.localeCompare(b)) + + // Create datasets for each relayer + const datasets = sortedRelayers.map((relayer) => { + const dateMap = dataMatrix.get(relayer)! + const data = sortedDates.map((date) => dateMap.get(date) || 0) + return { + label: relayer, + data, + backgroundColor: getColorFromRelayer(relayer) + } + }) + + // Set chart data with prepared labels and datasets + setChartData({ + labels: sortedDates, + datasets + }) + } + + useEffect(() => { + // Process data grouped by chain + const chainMap: Record = {} + + analyticsData4 + .filter((entry: Entry) => entry.IBC_Counterpart !== null && entry.IBC_Counterpart !== 'secret') + .forEach((entry: Entry) => { + const chainBech32Prefix = entry.IBC_Counterpart + const chainName = bech32PrefixToChainName.get(chainBech32Prefix) || chainBech32Prefix + chainMap[chainName] ||= [] + chainMap[chainName].push(entry) + }) + + const sortedChains = Object.keys(chainMap).sort((a, b) => a.localeCompare(b)) + setChainLabels(sortedChains) + setChainMap(chainMap) + + // Generate marks for the slider + const marks: { value: number; label: string }[] = [] + const lettersSeen = new Set() + sortedChains.forEach((chainName, index) => { + const letter = chainName.charAt(0).toUpperCase() + if (!lettersSeen.has(letter)) { + marks.push({ value: index, label: letter }) + lettersSeen.add(letter) + } + }) + setMarks(marks) + + // Find the index of "osmosis" + const osmosisIndex = sortedChains.findIndex((chainName) => chainName.toLowerCase() === 'osmosis') + + // Determine the default index + const defaultIndex = osmosisIndex !== -1 ? osmosisIndex : 0 + setSelectedChainIndex(defaultIndex) + + // Initialize chart data with the default chain's data + if (sortedChains.length > 0) { + const initialChain = sortedChains[defaultIndex] + updateChartDataForChain(initialChain, chainMap[initialChain]) + } + }, [analyticsData4]) + + const handleSliderChange = (event: Event, newValue: number | number[]) => { + const index = newValue as number + setSelectedChainIndex(index) + + const selectedChain = chainLabels[index] + const chainEntries = chainMap[selectedChain] || [] + + updateChartDataForChain(selectedChain, chainEntries) + } + + const options = { + responsive: true, + animation: true, + maintainAspectRatio: false, + scales: { + x: { + stacked: true, + ticks: { + color: theme === 'dark' ? '#fff' : '#000', + callback: function (value: any, index: number) { + return new Date(chartData.labels[index]).toLocaleDateString(undefined, { + year: '2-digit', + month: '2-digit', + day: '2-digit' + }) + } + }, + grid: { + color: theme === 'dark' ? '#fff' : '#000', + alpha: 0.5, + display: false, + drawOnChartArea: true, + drawTicks: true, + tickLength: 0 + }, + border: { + color: theme === 'dark' ? '#fff' : '#000' + } + }, + y: { + beginAtZero: true, + stacked: true, + ticks: { + color: theme === 'dark' ? '#fff' : '#000', + callback: function (value: any) { + return formatNumber(value, 2) + } + }, + border: { + color: theme === 'dark' ? '#fff' : '#000' + }, + grid: { + color: theme === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)', + display: true, + drawOnChartArea: true, + drawTicks: true, + tickLength: 0 + } + } + }, + plugins: { + legend: { + display: false + }, + tooltip: { + xAlign: 'center', + color: theme === 'dark' ? '#fff' : '#000', + callbacks: { + label: function (context: any) { + if (context.parsed.y !== null) { + return `${context.dataset.label}: ${formatNumber(context.parsed.y)} Transactions` + } + return '' + } + } + } + } + } + + function getColorFromRelayer(relayer: string) { + // Generate a unique color based on the relayer name + let hash = 0 + for (let i = 0; i < relayer.length; i++) { + hash = relayer.charCodeAt(i) + ((hash << 5) - hash) + } + const color = `#${(hash & 0x00ffffff).toString(16).padStart(6, '0').slice(-6)}` + return color + } + + return ( + <> +
+

+ {`IBC Transactions by Date and Relayer for ${chainLabels[selectedChainIndex]}`} + + + + + +

+
+
+ {chartData ? : null} +
+
+ {/* Remove the separate selected chain name display */} +
+ Chain: + chainLabels[value]} + sx={{ + color: theme === 'dark' ? '#fff' : '#000', // Set the slider color based on the theme + '& .MuiSlider-thumb': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Thumb color + }, + '& .MuiSlider-track': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Track color + }, + '& .MuiSlider-rail': { + backgroundColor: theme === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)' // Rail color + }, + '& .MuiSlider-mark': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Marks color + }, + '& .MuiSlider-markLabel': { + color: theme === 'dark' ? '#fff' : '#000' // Mark labels color + } + }} + /> +
+
+ + ) +} diff --git a/src/pages/analytics/components/RelayerChartWithDateSlider.tsx b/src/pages/analytics/components/RelayerChartWithDateSlider.tsx new file mode 100644 index 00000000..025ee5a3 --- /dev/null +++ b/src/pages/analytics/components/RelayerChartWithDateSlider.tsx @@ -0,0 +1,295 @@ +import { useContext, useEffect, useState } from 'react' +import { bech32PrefixToChainName, formatNumber } from 'utils/commons' +import Tooltip from '@mui/material/Tooltip' +import Slider from '@mui/material/Slider' +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + BarElement, + Tooltip as ChartTooltip, + Legend, + BarController +} from 'chart.js' +import { Bar } from 'react-chartjs-2' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faInfoCircle } from '@fortawesome/free-solid-svg-icons' +import { useUserPreferencesStore } from 'store/UserPreferences' +import { APIContext } from 'context/APIContext' +import { chains } from 'utils/config' + +ChartJS.register(CategoryScale, LinearScale, BarElement, ChartTooltip, Legend, BarController) + +type Entry = { + Date: string + IBC_Counterpart: string + Relayer: string + Transactions: number +} + +export default function RelayerChartWithDateSlider() { + const { theme } = useUserPreferencesStore() + const { analyticsData4 } = useContext(APIContext) + const [chartData, setChartData] = useState(null) + const [dates, setDates] = useState([]) + const [selectedDateIndex, setSelectedDateIndex] = useState(0) + + useEffect(() => { + // Process data grouped by date + const dateMap: Record = {} + + analyticsData4 + .filter((entry: Entry) => entry.IBC_Counterpart !== null && entry.IBC_Counterpart !== 'secret') + .forEach((entry: Entry) => { + const date = new Date(entry.Date).toISOString().split('T')[0] + dateMap[date] ||= [] + dateMap[date].push(entry) + }) + + const sortedDates = Object.keys(dateMap).sort((a, b) => new Date(a).getTime() - new Date(b).getTime()) + setDates(sortedDates) + + if (sortedDates.length > 0) { + const latestIndex = sortedDates.length - 1 + const latestDate = sortedDates[latestIndex] + setSelectedDateIndex(latestIndex) // Set to the latest date index + updateChartDataForDate(latestDate, dateMap[latestDate]) + } + }, [analyticsData4]) + + const updateChartDataForDate = (date: string, entries: Entry[]) => { + // Initialize data structures + const dataMatrix: Map> = new Map() + const chainsSet: Set = new Set() + const relayersSet: Set = new Set() + + // Build dataMatrix and collect unique chains and relayers + for (const entry of entries) { + const chainBech32Prefix = entry.IBC_Counterpart + const relayer = entry.Relayer || 'Other' + + chainsSet.add(chainBech32Prefix) + relayersSet.add(relayer) + + if (!dataMatrix.has(relayer)) { + dataMatrix.set(relayer, new Map()) + } + const chainMap = dataMatrix.get(relayer)! + chainMap.set(chainBech32Prefix, (chainMap.get(chainBech32Prefix) || 0) + entry.Transactions) + } + + // Prepare an array of { prefix, label } pairs + const prefixLabelPairs = Array.from(chainsSet).map((prefix) => ({ + prefix, + label: bech32PrefixToChainName.get(prefix) || prefix + })) + + // Sort prefixLabelPairs by label alphabetically + prefixLabelPairs.sort((a, b) => a.label.localeCompare(b.label)) + + // Extract the sorted bech32Prefixes and labels + const sortedBech32Prefixes = prefixLabelPairs.map((pair) => pair.prefix) + const labels = prefixLabelPairs.map((pair) => pair.label) + + // Create datasets for each relayer + const datasets = Array.from(relayersSet).map((relayer) => { + const chainMap = dataMatrix.get(relayer)! + const data = sortedBech32Prefixes.map((prefix) => chainMap.get(prefix) || 0) + return { + label: relayer, + data, + backgroundColor: getColorFromRelayer(relayer) + } + }) + + // Set chart data with prepared labels and datasets + setChartData({ + labels, + datasets + }) + } + + const handleSliderChange = (event: Event, newValue: number | number[]) => { + const index = newValue as number + setSelectedDateIndex(index) + + const selectedDate = dates[index] + const dateEntries = analyticsData4.filter( + (entry: Entry) => new Date(entry.Date).toISOString().split('T')[0] === selectedDate + ) + + updateChartDataForDate(selectedDate, dateEntries) + } + + const getSliderMarks = () => { + if (dates.length === 0) return [] + + const numMarks = Math.min(10, dates.length) // Limit to 10 marks max + const marks = [] + + const formatDate = (date: string) => + new Date(date).toLocaleDateString(undefined, { + year: '2-digit', + month: '2-digit', + day: '2-digit' + }) + + // Always include the first date + marks.push({ + value: 0, + label: formatDate(dates[0]) + }) + + if (dates.length > 1) { + const interval = (dates.length - 1) / (numMarks - 1) + + // Add intermediate marks only if necessary + for (let i = 1; i < numMarks - 1; i++) { + const index = Math.round(i * interval) + marks.push({ + value: index, + label: formatDate(dates[index]) + }) + } + + // Always include the last date + marks.push({ + value: dates.length - 1, + label: formatDate(dates[dates.length - 1]) + }) + } + + return marks + } + + const options = { + responsive: true, + animation: true, + maintainAspectRatio: false, + scales: { + x: { + stacked: true, + ticks: { + color: theme === 'dark' ? '#fff' : '#000' + }, + grid: { + color: theme === 'dark' ? '#fff' : '#000', + alpha: 0.5, + display: false, + drawOnChartArea: true, + drawTicks: true, + tickLength: 0 + }, + border: { + color: theme === 'dark' ? '#fff' : '#000' + } + }, + y: { + beginAtZero: true, + stacked: true, + ticks: { + color: theme === 'dark' ? '#fff' : '#000', + callback: function (value: any) { + return formatNumber(value, 2) + } + }, + border: { + color: theme === 'dark' ? '#fff' : '#000' + }, + grid: { + color: theme === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)', + display: true, + drawOnChartArea: true, + drawTicks: true, + tickLength: 0 + } + } + }, + plugins: { + legend: { + display: false + }, + tooltip: { + xAlign: 'center', + color: theme === 'dark' ? '#fff' : '#000', + callbacks: { + label: function (context: any) { + if (context.parsed.y !== null) { + return `${context.dataset.label}: ${formatNumber(context.parsed.y)} Transactions` + } + return '' + } + } + } + } + } + + function getColorFromRelayer(relayer: string) { + // Generate a unique color based on the relayer name + let hash = 0 + for (let i = 0; i < relayer.length; i++) { + hash = relayer.charCodeAt(i) + ((hash << 5) - hash) + } + const color = `#${(hash & 0x00ffffff).toString(16).padStart(6, '0').slice(-6)}` + return color + } + + return ( + <> +
+

+ IBC Transactions by Chain and Relayer + + + + + +

+
+
+ {chartData ? : null} +
+
+
+ + new Date(dates[value]).toLocaleDateString(undefined, { + year: '2-digit', + month: '2-digit', + day: '2-digit' + }) + } + sx={{ + color: theme === 'dark' ? '#fff' : '#000', // Set the slider color based on the theme + '& .MuiSlider-thumb': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Thumb color + }, + '& .MuiSlider-track': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Track color + }, + '& .MuiSlider-rail': { + backgroundColor: theme === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)' // Rail color (unfilled part of the slider) + }, + '& .MuiSlider-mark': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Marks color + }, + '& .MuiSlider-markLabel': { + color: theme === 'dark' ? '#fff' : '#000' // Mark labels color + } + }} + /> +
+
+ + ) +} diff --git a/src/pages/analytics/components/RelayerChartWithProviderSlider.tsx b/src/pages/analytics/components/RelayerChartWithProviderSlider.tsx new file mode 100644 index 00000000..7fafd244 --- /dev/null +++ b/src/pages/analytics/components/RelayerChartWithProviderSlider.tsx @@ -0,0 +1,285 @@ +import { useContext, useEffect, useState } from 'react' +import { bech32PrefixToChainName, formatNumber } from 'utils/commons' +import Tooltip from '@mui/material/Tooltip' +import Slider from '@mui/material/Slider' +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + BarElement, + Tooltip as ChartTooltip, + Legend, + BarController +} from 'chart.js' +import { Bar } from 'react-chartjs-2' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faInfoCircle } from '@fortawesome/free-solid-svg-icons' +import { useUserPreferencesStore } from 'store/UserPreferences' +import { APIContext } from 'context/APIContext' +import { chains } from 'utils/config' + +ChartJS.register(CategoryScale, LinearScale, BarElement, ChartTooltip, Legend, BarController) + +type Entry = { + Date: string + IBC_Counterpart: string + Relayer: string + Transactions: number +} + +export default function RelayerChartWithProviderSlider() { + const { theme } = useUserPreferencesStore() + const { analyticsData4 } = useContext(APIContext) + const [chartData, setChartData] = useState(null) + const [relayers, setRelayers] = useState([]) + const [selectedRelayerIndex, setSelectedRelayerIndex] = useState(0) + const [marks, setMarks] = useState<{ value: number; label: string }[]>([]) + + useEffect(() => { + // Process data grouped by relayer + const relayerMap: Record = {} + + analyticsData4 + .filter((entry: Entry) => entry.IBC_Counterpart !== null && entry.IBC_Counterpart !== 'secret') + .forEach((entry: Entry) => { + const relayer = entry.Relayer || 'Other' + relayerMap[relayer] ||= [] + relayerMap[relayer].push(entry) + }) + + const sortedRelayers = Object.keys(relayerMap).sort((a, b) => a.localeCompare(b)) + setRelayers(sortedRelayers) + + // Calculate total transactions per relayer + const relayerTotals: Record = {} + Object.entries(relayerMap).forEach(([relayer, entries]) => { + relayerTotals[relayer] = entries.reduce((sum, entry) => sum + entry.Transactions, 0) + }) + + // Find the relayer with the highest total transactions + const maxTotal = Math.max(...Object.values(relayerTotals)) + const topRelayers = Object.keys(relayerTotals).filter((relayer) => relayerTotals[relayer] === maxTotal) + // If multiple relayers have the same max total, choose the first one alphabetically + const defaultRelayer = topRelayers.sort((a, b) => a.localeCompare(b))[0] + + // Find the index of the default relayer + const defaultIndex = sortedRelayers.indexOf(defaultRelayer) + setSelectedRelayerIndex(defaultIndex >= 0 ? defaultIndex : 0) + + // Generate marks for the slider + const marks: { value: number; label: string }[] = [] + const lettersSeen = new Set() + sortedRelayers.forEach((relayerName, index) => { + const letter = relayerName.charAt(0).toUpperCase() + if (!lettersSeen.has(letter)) { + marks.push({ value: index, label: letter }) + lettersSeen.add(letter) + } + }) + setMarks(marks) + + // Initialize chart data with the default relayer's data + if (defaultRelayer) { + updateChartDataForRelayer(defaultRelayer, relayerMap[defaultRelayer]) + } + }, [analyticsData4]) + + const updateChartDataForRelayer = (relayer: string, entries: Entry[]) => { + // Initialize data structures + const dataMatrix: Map> = new Map() + const datesSet: Set = new Set() + const chainsSet: Set = new Set() + + // Build dataMatrix and collect unique dates and chains + for (const entry of entries) { + const date = new Date(entry.Date).toISOString().split('T')[0] + const chainBech32Prefix = entry.IBC_Counterpart + + datesSet.add(date) + chainsSet.add(chainBech32Prefix) + + if (!dataMatrix.has(chainBech32Prefix)) { + dataMatrix.set(chainBech32Prefix, new Map()) + } + const dateMap = dataMatrix.get(chainBech32Prefix)! + dateMap.set(date, (dateMap.get(date) || 0) + entry.Transactions) + } + + // Sort dates + const sortedDates = Array.from(datesSet).sort((a, b) => new Date(a).getTime() - new Date(b).getTime()) + + // Prepare an array of { prefix, label } pairs for chains + const prefixLabelPairs = Array.from(chainsSet).map((prefix) => ({ + prefix, + label: bech32PrefixToChainName.get(prefix) || prefix + })) + + // Sort prefixLabelPairs by label alphabetically + prefixLabelPairs.sort((a, b) => a.label.localeCompare(b.label)) + + // Extract the sorted bech32Prefixes and labels + const sortedBech32Prefixes = prefixLabelPairs.map((pair) => pair.prefix) + const chainLabels = prefixLabelPairs.map((pair) => pair.label) + + // Create datasets for each chain + const datasets = sortedBech32Prefixes.map((chainPrefix, index) => { + const dateMap = dataMatrix.get(chainPrefix)! + const data = sortedDates.map((date) => dateMap.get(date) || 0) + return { + label: chainLabels[index], + data, + backgroundColor: getColorFromChain(chainLabels[index]) + } + }) + + // Set chart data with prepared labels and datasets + setChartData({ + labels: sortedDates, + datasets + }) + } + + const handleSliderChange = (event: Event, newValue: number | number[]) => { + const index = newValue as number + setSelectedRelayerIndex(index) + + const selectedRelayer = relayers[index] + const relayerEntries = analyticsData4.filter((entry: Entry) => (entry.Relayer || 'Other') === selectedRelayer) + + updateChartDataForRelayer(selectedRelayer, relayerEntries) + } + + const options = { + responsive: true, + animation: true, + maintainAspectRatio: false, + scales: { + x: { + stacked: true, + ticks: { + color: theme === 'dark' ? '#fff' : '#000', + callback: function (value: any, index: number) { + return new Date(chartData.labels[index]).toLocaleDateString(undefined, { + year: '2-digit', + month: '2-digit', + day: '2-digit' + }) + } + }, + grid: { + color: theme === 'dark' ? '#fff' : '#000', + alpha: 0.5, + display: false, + drawOnChartArea: true, + drawTicks: true, + tickLength: 0 + }, + border: { + color: theme === 'dark' ? '#fff' : '#000' + } + }, + y: { + beginAtZero: true, + stacked: true, + ticks: { + color: theme === 'dark' ? '#fff' : '#000', + callback: function (value: any) { + return formatNumber(value, 2) + } + }, + border: { + color: theme === 'dark' ? '#fff' : '#000' + }, + grid: { + color: theme === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)', + display: true, + drawOnChartArea: true, + drawTicks: true, + tickLength: 0 + } + } + }, + plugins: { + legend: { + display: false + }, + tooltip: { + xAlign: 'center', + color: theme === 'dark' ? '#fff' : '#000', + callbacks: { + label: function (context: any) { + if (context.parsed.y !== null) { + return `${context.dataset.label}: ${formatNumber(context.parsed.y)} Transactions` + } + return '' + } + } + } + } + } + + function getColorFromChain(chain: string) { + // Generate a unique color based on the chain name + let hash = 0 + for (let i = 0; i < chain.length; i++) { + hash = chain.charCodeAt(i) + ((hash << 5) - hash) + } + const color = `#${(hash & 0x00ffffff).toString(16).padStart(6, '0').slice(-6)}` + return color + } + + return ( + <> +
+

+ IBC Transactions by Date and Chain (Per Relayer) + + + + + +

+
+
+ {chartData ? : null} +
+
+
+ Relayer: + relayers[value]} + sx={{ + color: theme === 'dark' ? '#fff' : '#000', // Set the slider color based on the theme + '& .MuiSlider-thumb': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Thumb color + }, + '& .MuiSlider-track': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Track color + }, + '& .MuiSlider-rail': { + backgroundColor: theme === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)' // Rail color + }, + '& .MuiSlider-mark': { + backgroundColor: theme === 'dark' ? '#fff' : '#000' // Marks color + }, + '& .MuiSlider-markLabel': { + color: theme === 'dark' ? '#fff' : '#000' + } + }} + /> +
+
+ + ) +} diff --git a/src/pages/dashboard/components/StakingChart.tsx b/src/pages/dashboard/components/StakingChart.tsx index 215e0708..838d478e 100644 --- a/src/pages/dashboard/components/StakingChart.tsx +++ b/src/pages/dashboard/components/StakingChart.tsx @@ -52,17 +52,6 @@ export default function StakingChart() { }) useEffect(() => { - console.log( - bondedToken, - notBondedToken, - totalSupply, - communityPool, - sSCRTTokenSupply, - stkdSCRTTokenSupply, - IBCTokenSupply, - exchangesTokenSupply, - burnedTokenSupply - ) if ( bondedToken && notBondedToken && diff --git a/src/store/TokenPrices.ts b/src/store/TokenPrices.ts index a7907335..f019bd80 100644 --- a/src/store/TokenPrices.ts +++ b/src/store/TokenPrices.ts @@ -23,9 +23,8 @@ export const useTokenPricesStore = create()((set, get) => ({ init: () => { let prices: CoinPrice[] - let coinGeckoIdsString: string = allTokens.map((token) => token.coingecko_id).join(',') - - console.log(coinGeckoIdsString) + /*let coinGeckoIdsString: string = allTokens.map((token) => token.coingecko_id).join(',') + console.log(coinGeckoIdsString)*/ // fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${coinGeckoIdsString}&vs_currencies=USD`) fetch(`https://priceapibuffer.secretsaturn.net/getPrices`) diff --git a/src/utils/commons.ts b/src/utils/commons.ts index d16cbb89..e345e00e 100644 --- a/src/utils/commons.ts +++ b/src/utils/commons.ts @@ -1,5 +1,5 @@ import { Currency } from 'types/Currency' -import { tokens, snips, ICSTokens } from './config' +import { tokens, snips, ICSTokens, chains } from './config' import mixpanel from 'mixpanel-browser' import { SecretNetworkClient } from 'secretjs' @@ -20,6 +20,12 @@ export const randomDelay = (min: number, max: number) => { export const allTokens = tokens.concat(snips).concat(ICSTokens) +// Cache the mapping from bech32 prefixes to chain names +export const bech32PrefixToChainName: Map = new Map() +for (const chainInfo of Object.values(chains)) { + bech32PrefixToChainName.set(chainInfo.bech32_prefix, chainInfo.chain_name) +} + /** * Generates random string of characters, used to add entropy to TX data * */