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
* */