diff --git a/.env.production b/.env.production deleted file mode 100644 index 0b333d0..0000000 --- a/.env.production +++ /dev/null @@ -1,2 +0,0 @@ -INFURA_API='8QAV9SdDHssoGB8NGQO2dZ4YnF7nKRhe' -VITE_SN_PVT_KEY='0x03fd5b55658a4801f5f25a40414815a499af43c0a09a20abc7521bf8bd74463f' \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8399359..bc1e2ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-query": "^3.39.2", + "react-responsive": "^9.0.0", "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", "recharts": "^2.1.13", @@ -7047,6 +7048,11 @@ "node": ">=10" } }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==" + }, "node_modules/css-minimizer-webpack-plugin": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", @@ -10881,6 +10887,11 @@ "node": ">=10.17.0" } }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -14070,6 +14081,14 @@ "remove-accents": "0.4.2" } }, + "node_modules/matchmediaquery": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz", + "integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==", + "dependencies": { + "css-mediaquery": "^0.1.2" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -16971,6 +16990,23 @@ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-responsive": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-9.0.0.tgz", + "integrity": "sha512-hyJmKpAglo1AdZsGmu1sab0VInEGzUTXT3nJ3Cl7cMDjR0ffV30lOM3N1xag456OsCGAgqRIkgloLy6tHaqZ2w==", + "dependencies": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.3.0", + "prop-types": "^15.6.1", + "shallow-equal": "^1.2.1" + }, + "engines": { + "node": ">=0.10" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-router": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz", @@ -18026,6 +18062,11 @@ "sha.js": "bin.js" } }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -25889,6 +25930,11 @@ } } }, + "css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==" + }, "css-minimizer-webpack-plugin": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", @@ -28641,6 +28687,11 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -30940,6 +30991,14 @@ "remove-accents": "0.4.2" } }, + "matchmediaquery": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz", + "integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==", + "requires": { + "css-mediaquery": "^0.1.2" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -32840,6 +32899,17 @@ "lodash": "^4.17.21" } }, + "react-responsive": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-9.0.0.tgz", + "integrity": "sha512-hyJmKpAglo1AdZsGmu1sab0VInEGzUTXT3nJ3Cl7cMDjR0ffV30lOM3N1xag456OsCGAgqRIkgloLy6tHaqZ2w==", + "requires": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.3.0", + "prop-types": "^15.6.1", + "shallow-equal": "^1.2.1" + } + }, "react-router": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz", @@ -33640,6 +33710,11 @@ "safe-buffer": "^5.0.1" } }, + "shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index b651376..92a483c 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-query": "^3.39.2", + "react-responsive": "^9.0.0", "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", "recharts": "^2.1.13", diff --git a/public/_redirects b/public/_redirects deleted file mode 100644 index 6de632c..0000000 --- a/public/_redirects +++ /dev/null @@ -1 +0,0 @@ -/api/* http://170.187.225.159:3000/:splat 200 diff --git a/src/App.css b/src/App.css index 49eb42f..f18087a 100644 --- a/src/App.css +++ b/src/App.css @@ -29,11 +29,16 @@ body { overflow-y: auto; display:block; } + thead{ position:sticky; top:0 } +input:checked ~ .dot { + transform: translateX(100%); + background-color: #fb7185; +} ::-webkit-scrollbar { width: 12px; @@ -50,7 +55,6 @@ thead{ ::-webkit-scrollbar-thumb:hover { background-color: #0d527a; - /* cursor: pointer; */ } ::-webkit-scrollbar-track { @@ -59,106 +63,8 @@ thead{ ::-webkit-scrollbar-button:single-button { background-color: #081128; - /* background-size: 8px; */ - /* background-repeat: no-repeat; */ -} -/* -::-webkit-scrollbar-button:single-button:vertical:decrement { - height: 10px; - width: 10px; - background-position: center 4px; - background-image: url("data:image/svg+xml;utf8,"); -} - -::-webkit-scrollbar-button:single-button:vertical:decrement:hover { - background-image: url("data:image/svg+xml;utf8,"); -} - -::-webkit-scrollbar-button:single-button:vertical:increment { - height: 10px; - width: 10px; - background-position: center 4px; - background-image: url("data:image/svg+xml;utf8,"); -} - -::-webkit-scrollbar-button:vertical:single-button:increment:hover { - background-image: url("data:image/svg+xml;utf8,"); -} */ - -/* -.menus { - display: flex; - list-style: none; -} - -.menu-items { - position: relative; - font-size: 14px; -} - -.menu-items a { - display: block; - font-size: inherit; - color: inherit; - text-decoration: none; } -.menu-items button { - color: inherit; - font-size: inherit; - border: none; - background-color: transparent; - cursor: pointer; - width: 100%; -} - -.menu-items a, -.menu-items button { - text-align: left; - padding: 0.7rem 1rem; -} - -.menu-items a:hover, -.menu-items button:hover { - background-color: #f2f2f2; -} - -.arrow::after { - content: ""; - display: inline-block; - margin-left: 0.28em; - vertical-align: 0.09em; - border-top: 0.42em solid; - border-right: 0.32em solid transparent; - border-left: 0.32em solid transparent; -} - -.dropdown { - position: absolute; - right: 0; - left: auto; - box-shadow: 0 10px 15px -3px rgba(46, 41, 51, 0.08), - 0 4px 6px -2px rgba(71, 63, 79, 0.16); - font-size: 0.875rem; - z-index: 9999; - min-width: 10rem; - padding: 0.5rem 0; - list-style: none; - background-color: #fff; - border-radius: 0.5rem; - display: none; -} - -.dropdown.show { - display: block; -} - -.dropdown .dropdown-submenu { - position: absolute; - left: 100%; - top: -7px; -} */ - @tailwind base; @tailwind components; @tailwind utilities; diff --git a/src/App.tsx b/src/App.tsx index 9c3f65d..541189f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,28 +1,35 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'; -import { Test, Main, Disclaimer } from './pages'; +import { Main, Disclaimer, TermsOfUse } from './pages'; import { Navbar } from './components/navbar'; import { Footer } from './components/footer'; import { PriceContextProvider } from './context/PriceContext'; +import { AppContextProvider } from './context/AppContext'; function App() { return ( -
+
+ StarkStation is Alpha! +
+
- - - } /> - + + + + } /> + + + + } /> + - - } /> - + + } /> + - {/* - } /> - */} -
+
+
diff --git a/src/components/Test/BlockLatency/BlockLatencyChart.tsx b/src/components/Test/BlockLatency/BlockLatencyChart.tsx deleted file mode 100644 index 5b76dae..0000000 --- a/src/components/Test/BlockLatency/BlockLatencyChart.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -import dayjs from 'dayjs'; -import { SpinnerCircular } from "spinners-react"; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; -import Numeral from 'numeral'; - -export default function BlockLatencyChart(props: { - data:any - isLoading:boolean - chain: string - avgBlockLatency: number -}) { - const { data,isLoading, chain, avgBlockLatency } = props; - - return ( -
-
-

{chain==="ethereum"?"Ethereum":"StarkNet"}

-
-

Average BlockLatency (last 50 blocks):

-

{isNaN(avgBlockLatency)?"":avgBlockLatency.toFixed(2)} seconds

-
-
- {!isLoading ? ( - - - - toNiceDate(tick)} - /> - toK(tick)} - interval="preserveEnd" - fontSize={10} - /> - toK(val)} - labelFormatter={label => toNiceDateYear(label)} - labelStyle={{ paddingTop: 4, paddingBottom: 4 }} - contentStyle={{ - padding: '10px 14px', - borderRadius: 10, - border: 'none', - backgroundColor: '#0369a1', - color: '#fff', - fontSize: '12px', - }} - wrapperStyle={{ top: -70, left: -10, outline: 'none' }} - /> - - - - ) : ( -
- -
- )} -
- ); -} - - -export const toK = (num:any) => { - return Numeral(num).format('0.[0000]a'); -}; - -export const toNiceDateYear = (date:any) => dayjs(date).format('MMMM DD, YYYY h:mm:ss A'); - -export const toNiceDate = (date:any) => { - let x = dayjs(date).format('DD/MM h:mm A'); - return x; -}; diff --git a/src/components/Test/BlockLatency/BlockLatencyPanel.tsx b/src/components/Test/BlockLatency/BlockLatencyPanel.tsx deleted file mode 100644 index 4af0845..0000000 --- a/src/components/Test/BlockLatency/BlockLatencyPanel.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useState, useEffect, useMemo } from 'react'; -import dayjs from 'dayjs'; -import BlockLatencyChart from './BlockLatencyChart'; - - -export default function BlockLatencyPanel(props: { - isLoading:boolean - data:any - chain: string -}) { - const { data, isLoading, chain } = props; - const [blockLatencyData, setBlockLatencyData] = useState<{ time: number; blockLatency: number;}[]>([]) - const [avgBlockLatency, setAvgBlockLatency] = useState(0) - - const blockLatency_dataM = useMemo(() => { - let blockLatency_data: { - time: number; - blockLatency: number; - }[] = []; - if (Object.keys(data).length >0) { - Object.keys(data).forEach((k:any) => { - if (Number(k) < Object.keys(data).length -2){ - const time_taken = dayjs(data[k].timestamp).diff(dayjs(data[Number(k)+1].timestamp),'seconds'); - - blockLatency_data[k] = { - time: data[k].timestamp, - blockLatency: time_taken, - }; - } - }); - } - return blockLatency_data}, [data]); - - useEffect(() => { - if (data) { - setBlockLatencyData(blockLatency_dataM.reverse()) - const avg = blockLatency_dataM.reduce((r, c) => r + c.blockLatency, 0) / blockLatency_dataM.length; - setAvgBlockLatency(avg); - } - }, [data]) - - - return ( -
- -
- ); -} - diff --git a/src/components/Test/BlockLatency/index.tsx b/src/components/Test/BlockLatency/index.tsx deleted file mode 100644 index a19a788..0000000 --- a/src/components/Test/BlockLatency/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export { default as BlockLatencyChart } from './BlockLatencyChart'; -export { default as BlockLatencyPanel } from './BlockLatencyPanel'; \ No newline at end of file diff --git a/src/components/Test/Distribution/DistributionChart.tsx b/src/components/Test/Distribution/DistributionChart.tsx deleted file mode 100644 index dac66e0..0000000 --- a/src/components/Test/Distribution/DistributionChart.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -import dayjs from 'dayjs'; -import { SpinnerCircular } from "spinners-react"; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; -import Numeral from 'numeral'; - -export default function DistributionChart(props: { - data:any - isLoading:boolean - chain: string - variance: number -}) { - const { data,isLoading, chain, variance } = props; - - return ( -
-
-

{chain==="ethereum"?"Ethereum":"StarkNet"}

-
-

Variance (last 50 blocks):

-

{isNaN(variance)?"":toK(variance)}

-
-
- {!isLoading ? ( - - - - toK(tick)} - // tickFormatter={tick => toNiceDate(tick)} - /> - toK(tick)} - interval="preserveEnd" - fontSize={10} - /> - toK(val)} - labelFormatter={label => toKe(label)} - labelStyle={{ paddingTop: 4, paddingBottom: 4 }} - contentStyle={{ - padding: '10px 14px', - borderRadius: 10, - border: 'none', - backgroundColor: '#0369a1', - color: '#fff', - fontSize: '12px', - }} - wrapperStyle={{ top: -70, left: -10, outline: 'none' }} - /> - - - - ) : ( -
- -
- )} -
- ); -} - - -export const toK = (num:any) => { - return Numeral(num).format('0.[0000]a'); -}; - -export const toKe = (num:any) => { - return 'BlockTime : ' + Numeral(num).format('0.[0000]a') + ' (sec)'; -}; - -export const toNiceDateYear = (date:any) => dayjs(date).format('MMMM DD, YYYY h:mm:ss A'); - -export const toNiceDate = (date:any) => { - let x = dayjs(date).format('DD/MM h:mm A'); - return x; -}; diff --git a/src/components/Test/Distribution/DistributionPanel.tsx b/src/components/Test/Distribution/DistributionPanel.tsx deleted file mode 100644 index 3cf47c1..0000000 --- a/src/components/Test/Distribution/DistributionPanel.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { useState, useEffect, useMemo } from 'react'; -import dayjs from 'dayjs'; -import DistributionChart from './DistributionChart'; - -// Calculate mean -const calculateMean = (values:number[]) => { - const mean = (values.reduce((sum, current) => sum + current)) / values.length; - return mean; -} - -// Calculate variance -const calculateVariance= (values:number[]) => { - const average = calculateMean(values); - const squareDiffs = values.map((value) => { - const diff = value - average; - return diff * diff; - }) - - const variance = calculateMean(squareDiffs); - return variance; -} - -// Calculate stand deviation -const calculateSD = (values:number[]) => { - const variance = calculateVariance(values); - return Math.sqrt(variance); -} - -// Calculate stand deviation -function NormalDensityZx(x:number, Mean:number, StdDev:number) { - var a = x - Mean; - return Math.exp(-(a * a) / (2 * StdDev * StdDev)) / (Math.sqrt(2 * Math.PI) * StdDev); -} - -export default function DistributionPanel(props: { - isLoading:boolean - data:any - chain: string -}) { - const { data, isLoading, chain } = props; - const [distributionData, setDistributionData] = useState<{ value: number; normalDensityZx: number;}[]>([]) - const [variance, setVariance] = useState(0) - - const distribution_dataM = useMemo(() => { - let distribution_data: { - value: number; - normalDensityZx: number; - }[] = []; - let latency_values: number[]=[]; - - if (Object.keys(data).length >0) { - Object.keys(data).forEach((k:any) => { - if (Number(k) < Object.keys(data).length -2){ - const time_taken = dayjs(data[k].timestamp).diff(dayjs(data[Number(k)+1].timestamp),'seconds'); - latency_values[k] = time_taken; - } - }); - } - - if (latency_values.length >0){ - const sd = calculateSD(latency_values); - const mean = calculateMean(latency_values); - setVariance(calculateVariance(latency_values)) - latency_values.forEach((x, i) => { - var dp = {value:x,normalDensityZx:NormalDensityZx(x, mean, sd)}; - distribution_data.push(dp); - }); - } - - - // console.log(JSON.stringify(distribution_data)) - return distribution_data}, [data]); - - useEffect(() => { - if (data && Object.keys(distribution_dataM).length>0) { - const sortedDistribution = distribution_dataM.sort((a:any,b:any)=> a.value-b.value) - setDistributionData(sortedDistribution) - } - }, [data]) - - - return ( -
- -
- ); -} - diff --git a/src/components/Test/Distribution/index.ts b/src/components/Test/Distribution/index.ts deleted file mode 100644 index 5b30a13..0000000 --- a/src/components/Test/Distribution/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as DistributionChart } from './DistributionChart'; -export { default as DistributionPanel } from './DistributionPanel'; \ No newline at end of file diff --git a/src/components/Test/Tpb/TpbChart.tsx b/src/components/Test/Tpb/TpbChart.tsx deleted file mode 100644 index 4ec4b0a..0000000 --- a/src/components/Test/Tpb/TpbChart.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -import dayjs from 'dayjs'; -import { SpinnerCircular } from "spinners-react"; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; -import Numeral from 'numeral'; - -export default function TpbChart(props: { - data:any - isLoading:boolean - chain: string - avgTpb: number -}) { - const { data,isLoading, chain, avgTpb } = props; - - return ( -
-
-

{chain==="ethereum"?"Ethereum":"StarkNet"}

-
-

Average TPB (last 50 blocks):

-

{isNaN(avgTpb)?"":avgTpb}

-
-
- {!isLoading ? ( - - - - toNiceDate(tick)} - /> - toK(tick)} - interval="preserveEnd" - fontSize={10} - /> - toK(val)} - labelFormatter={label => toNiceDateYear(label)} - labelStyle={{ paddingTop: 4, paddingBottom: 4 }} - contentStyle={{ - padding: '10px 14px', - borderRadius: 10, - border: 'none', - backgroundColor: '#0369a1', - color: '#fff', - fontSize: '12px', - }} - wrapperStyle={{ top: -70, left: -10, outline: 'none' }} - /> - - - - ) : ( -
- -
- )} -
- ); -} - - -export const toK = (num:any) => { - return Numeral(num).format('0.[0000]a'); -}; - -export const toNiceDateYear = (date:any) => dayjs(date).format('MMMM DD, YYYY h:mm:ss A'); - -export const toNiceDate = (date:any) => { - let x = dayjs(date).format('DD/MM h:mm A'); - return x; -}; diff --git a/src/components/Test/Tpb/TpbPanel.tsx b/src/components/Test/Tpb/TpbPanel.tsx deleted file mode 100644 index d4f1bfc..0000000 --- a/src/components/Test/Tpb/TpbPanel.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -import TpbChart from './TpbChart'; - - -export default function TpbPanel(props: { - isLoading:boolean - data:any - chain: string -}) { - const { data, isLoading, chain } = props; - const [tpbData, setTpbData] = useState<{ time: number; txnCount: number;}[]>([]) - const [avgTpb, setAvgTpb] = useState(0) - - useEffect(() => { - if (data) { - let tpb_data: { - time: number; - txnCount: number; - }[] = []; - Object.keys(data).forEach((k:any) => { - tpb_data[k] = { - time: data[k].timestamp, - txnCount: data[k].txnCount, - }; - }); - setTpbData(tpb_data.reverse()) - const avg = tpb_data.reduce((r, c) => r + c.txnCount, 0) / tpb_data.length; - setAvgTpb(avg) - } - }, [data]) - - - return ( -
- -
- ); -} - diff --git a/src/components/Test/Tpb/index.ts b/src/components/Test/Tpb/index.ts deleted file mode 100644 index c344d7a..0000000 --- a/src/components/Test/Tpb/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as TpbChart } from './TpbChart'; -export { default as TpbPanel } from './TpbPanel'; \ No newline at end of file diff --git a/src/components/Test/Tps/TpsChart.tsx b/src/components/Test/Tps/TpsChart.tsx deleted file mode 100644 index f65dd20..0000000 --- a/src/components/Test/Tps/TpsChart.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -import dayjs from 'dayjs'; -import { SpinnerCircular } from "spinners-react"; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; -import Numeral from 'numeral'; - -export default function TpsChart(props: { - data:any - isLoading:boolean - chain: string - avgTps: number -}) { - const { data,isLoading, chain, avgTps} = props; - - return ( -
-
-

{chain==="ethereum"?"Ethereum":"StarkNet"}

-
-

Average TPS (last 50 blocks):

-

{isNaN(avgTps)?"":avgTps.toFixed(4)}

-
-
- {!isLoading ? ( - - - - toNiceDate(tick)} - /> - toK(tick)} - interval="preserveEnd" - fontSize={10} - // minTickGap={80} - /> - toK(val)} - labelFormatter={label => toNiceDateYear(label)} - labelStyle={{ paddingTop: 4, paddingBottom: 4 }} - contentStyle={{ - padding: '10px 14px', - borderRadius: 10, - border: 'none', - backgroundColor: '#0369a1', - color: '#fff', - fontSize: '12px', - }} - wrapperStyle={{ top: -70, left: -10, outline: 'none' }} - /> - {/* */} - - - - ) : ( -
- -
- )} -
- ); -} - - -export const toK = (num:any) => { - return Numeral(num).format('0.[0000]a'); -}; - -export const toNiceDateYear = (date:any) => dayjs(date).format('MMMM DD, YYYY h:mm:ss A'); - -export const toNiceDate = (date:any) => { - let x = dayjs(date).format('DD/MM h:mm A'); - return x; -}; diff --git a/src/components/Test/Tps/TpsPanel.tsx b/src/components/Test/Tps/TpsPanel.tsx deleted file mode 100644 index 60b1458..0000000 --- a/src/components/Test/Tps/TpsPanel.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React, { useState, useEffect, useCallback,useMemo } from 'react'; -import TpsChart from './TpsChart'; -import dayjs from 'dayjs'; - -export default function TpsPanel(props: { - isLoading:boolean - data:{ [key in string]: {timestamp:number,txnCount:number} } - chain:string -}) { - const { data, isLoading, chain } = props; - const [tpsData, setTpsData] = useState<{ time: number; tps: number;}[]>([]) - const [avgTps, setAvgTps] = useState(0) - - const tps_dataM = useMemo(() => { - let tps_data: { - time: number; - tps: number; - }[] = []; - if (Object.keys(data).length >0) { - Object.keys(data).forEach((k:any) => { - if (Number(k) < Object.keys(data).length -2){ - const time_taken = dayjs(data[k].timestamp).diff(dayjs(data[Number(k)+1].timestamp),'seconds'); - const tx_count = data[k].txnCount; - - tps_data[k] = { - time: data[k].timestamp, - tps: tx_count/time_taken, - }; - } - }); - } - return tps_data}, [data]); - - useEffect(() => { - if (data) { - setTpsData(tps_dataM.reverse()) - const avg = tps_dataM.reduce((r, c) => r + c.tps, 0) / tps_dataM.length; - setAvgTps(avg); - - } - }, [data]) - - return ( -
- -
- ); -} diff --git a/src/components/Test/Tps/index.ts b/src/components/Test/Tps/index.ts deleted file mode 100644 index 7b17baf..0000000 --- a/src/components/Test/Tps/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as TpsChart } from './TpsChart'; -export { default as TpsPanel } from './TpsPanel'; \ No newline at end of file diff --git a/src/components/blocks/BlocksChart.tsx b/src/components/blocks/BlocksChart.tsx new file mode 100644 index 0000000..bec36f6 --- /dev/null +++ b/src/components/blocks/BlocksChart.tsx @@ -0,0 +1,220 @@ +import { useState, useEffect, useContext } from 'react'; + +import dayjs from 'dayjs'; +import { SpinnerCircular } from "spinners-react"; +import { LineChart, Line, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, Symbols, Surface } from 'recharts'; +import Numeral from 'numeral'; +import { AppContext } from '../../context/AppContext'; + +export default function BlocksChart(props: { + data:any + isLoading:boolean + chartDisplay:string + currency:string + timeFrame:string +}) { + const { data, isLoading, chartDisplay, currency, timeFrame } = props; + const [chartData, setChartData] = useState([]) + const [legendState, setLegendState] = useState([true,true,true]) + + useEffect(() => { + if (data) { + let uniqueTimeFrames:any = []; + const _chartData = data.filter((item:any) => { + const sn_value_exists = Object.keys(item).filter((key) => key.includes('sn_value')).length && item?.sn_value != null + const isDuplicate = uniqueTimeFrames.includes(item.time); + if (!isDuplicate && sn_value_exists) { + uniqueTimeFrames.push(item.time); + return true; + } + return false; + }) + setChartData(_chartData) + } + }, [data]) + + const displayUnit = chartDisplay === "avgGasUsed" ? "Gwei" : currency.toUpperCase() + const onlyY0 = legendState.filter(v => v).length === 1; + const isL1CostChart = chartDisplay === "verificationCost" ? true : false + const marginLeft = 5//chartDisplay === "verificationCost" || onlyY0 ? 45 : 5 + + const handleClick = (index:number) => { + if (legendState) { + const _legendState = legendState.map(a => {return a}); + _legendState[index] = !_legendState[index] + + if ( !isL1CostChart && _legendState.includes(true)) { + setLegendState(_legendState); + } + } + }; + + const renderLegend = (props:any) => { + const { payload } = props; + return ( +
+ { + payload.map((entry:any, index:any) => ( +
handleClick(index)} key={`item-${index}`} className={`flex justify-center items-center text-[${entry.color}] cursor-pointer hover:bg-gray-900 ${legendState[index]?"":'opacity-25'} rounded-md mx-1 px-1`}> + + + + {entry.value} +
+ )) + } +
+ ); + } + + return ( +
+ {!isLoading && data.length ? ( + + + toNiceDate(tick, timeFrame)} + padding={{ left: 20, right: 20 }} + /> + onlyY0 && legendState[2] ? toK_percent(tick):toK(tick)} + interval="preserveEnd" + fontSize={10} + fontFamily='Roboto Mono, monospace' + stroke={isL1CostChart || (!legendState[1] && legendState[0]) ? "#fb7185" : !legendState[1] ? "#b9b72b" : "#81cefa" } + padding={{ top: 20, bottom: 5 }} + /> + legendState[2]? toK_percent(tick): toK(tick)} + interval="preserveStartEnd" + fontSize={10} + fontFamily='Roboto Mono, monospace' + stroke={legendState[2]?"#b9b72b":"#fb7185"} + padding={{ top: 20, bottom: 5 }} + /> + } + wrapperStyle={{ top: -70, left: -10, outline: 'none' }} + /> + {!isL1CostChart ? : } + + {!isL1CostChart && } + {!isL1CostChart && } + + + ) : ( +
+ +
+ )} +
+ ); +} + + + +const CustomTooltip = (props:{ active:any, payload:any, label:any, timeFrame:string, displayUnit:string, isL1CostChart:boolean, legendState:boolean[] }) => { + const {active, payload, label, timeFrame, displayUnit, isL1CostChart, legendState} = props; + const { network } = useContext(AppContext) + if (active && payload && payload.length) { + const date = toNiceDateYear(label, timeFrame) + const snValue = toNiceValue(payload[0]?.value, displayUnit, network) + const ethValue = !isL1CostChart ? toNiceValue(payload[legendState[0] && legendState[1] ? 1 : 0]?.value, displayUnit, network) : '0' + const l2vsl1 = toK_percent_float(payload[legendState[0] && legendState[1] ? 2 : !legendState[0] && !legendState[1] ? 0 : 1]?.value) + return ( +
+

{date}

+ {!isL1CostChart ? + (<> + {legendState[0] &&

{`on Starknet : ${snValue}`}

} + {legendState[1] &&

{`on Ethereum : ${ethValue}`}

} + {legendState[2] &&

{`L2 vs L1 : ${l2vsl1}`}

} + ) + :( <> +

{`Verification Cost : ${snValue}`}

+ ) + } +
+ ); + } + + return null; + }; + +export const toK = (num:any) => { + return Numeral(num).format('0.[000000]a'); +}; + +export const toK_percent = (num:any) => { + return Numeral(num*100).format('0 a').concat(" %"); +}; + +export const toK_percent_float = (num:any) => { + return Numeral(num*100).format('0.[0000] a').concat(" %"); +}; + +export const toNiceValue = (num:any,unit:string,network:string) => { + if(network === 'goerli'){ + return (num.toFixed(9).concat(" ",unit)); + } else { + return (Numeral(num).format('0.[000000]a').concat(" ",unit)); + } +}; + +export const toNiceDateYear = (date:any, timeFrame:string) => { + let x = timeFrame === '1d' ? dayjs(date).format('MMM DD, YYYY'): dayjs(date).format('MMM DD, YYYY h:mm A'); + return x; +}; + +export const toNiceDate = (date:any, timeFrame:string) => { + let x = timeFrame === '1d' ? dayjs(date).format('DD/MM'): dayjs(date).format('DD/MM HH:mm'); + return x; +}; \ No newline at end of file diff --git a/src/components/blocks/BlocksPanel.tsx b/src/components/blocks/BlocksPanel.tsx new file mode 100644 index 0000000..875c613 --- /dev/null +++ b/src/components/blocks/BlocksPanel.tsx @@ -0,0 +1,263 @@ + +import { useState, useEffect, useContext, useMemo } from 'react'; +import dayjs from 'dayjs'; +import {ethers} from 'ethers'; +import { SpinnerCircular } from "spinners-react"; +import BlocksChart from './BlocksChart'; +import { AppContext } from '../../context/AppContext'; +import isBetween from 'dayjs/plugin/isBetween'; +dayjs.extend(isBetween) + +interface IData { + time: string; + value: number; + price: number; +} + +interface IChartData { + time: string; + sn_value?: number; + eth_value: number; + percent_change?: number; +} + +export default function BlocksPanel(props: { + snDetailLoading:boolean + snDetailData:any + snBlockLoading: boolean + snBlockData:any + ethDetailLoading:boolean + snProofData:any + snProofLoading:boolean + ethDetailData:any + timeFrame:string + setTimeFrame:any +}) { + const {snDetailLoading, snBlockLoading, ethDetailLoading, snProofLoading, snDetailData, snBlockData, snProofData, ethDetailData, timeFrame, setTimeFrame } = props; + const { network } = useContext(AppContext) + const [chartALoading, setChartALoading] = useState(false); + const [chartBLoading, setChartBLoading] = useState(false); + const [chartCLoading, setChartCLoading] = useState(false); + const [lastUpdated, setLastUpdated] = useState(0); + const [chartDisplay, setChartDisplay] = useState('verificationCost'); + const [chartData, setChartData] = useState([]); + const [currency, setCurrency] = useState('eth'); + + const [avgTxnFee_SN, setAvgTxnFee_SN] = useState([]); + const [avgGasUsed_SN, setAvgGasUsed_SN] = useState([]); + const [avgBlockVerificationCost_SN, setAvgBlockVerificationCost_SN] = useState([]); + const [avgTxnFee_ETH, setAvgTxnFee_ETH] = useState([]); + const [avgGasUsed_ETH, setAvgGasUsed_ETH] = useState([]); + + const snBlock = snBlockData?.detail[0]; + const snProof = snProofData?.detail[0]; + + const isCurrencyEth: boolean = currency === 'eth'? true : false; + + useEffect(() => { + const interval = setInterval(() => { + setLastUpdated(dayjs().diff(snBlockData?.lastUpdated, 'seconds')); + }, 1000); + + return () => clearInterval(interval); + }, [snBlockData]); + + useEffect(() => { + if(snDetailData){ + const _avgTxnFee: IData[] = snDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:Number(ethers?.utils.formatEther(item.avgTxnFee)), price:item?.ethPrice})); + const _avgGasUsed: IData[] = snDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.gasUsedPerblock, price:item?.ethPrice})); + const _avgBlockCost: IData[] = snDetailData.filter((item: any) => item.timestamp > 1666915050).map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.blockVerificationCost, price:item?.ethPrice})); + setAvgBlockVerificationCost_SN(_avgBlockCost); + setAvgTxnFee_SN(_avgTxnFee); + setAvgGasUsed_SN(_avgGasUsed); + } + }, [snDetailData]); + + useEffect(() => { + if(ethDetailData){ + const _avgTxnFee: IData[] = ethDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:Number(ethers?.utils.formatEther(item.avgTxnFee)), price:item?.ethPrice})); + const _avgGasUsed: IData[] = ethDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.gasUsedPerblock, price:item?.ethPrice})); + setAvgTxnFee_ETH(_avgTxnFee); + setAvgGasUsed_ETH(_avgGasUsed); + } + }, [ethDetailData]); + + useEffect(() => { + setChartALoading(true); + setChartBLoading(true); + setChartCLoading(true); + }, [network]); + + useEffect(() => { + if(chartDisplay){ + switch(chartDisplay) { + case "avgTxnFee": + let _avgTxnFee: IChartData[] = [] + avgTxnFee_ETH.forEach((v,i) => { + let snValue = avgTxnFee_SN.find((val) => val.time === v.time) + // let snValue = avgTxnFee_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + _avgTxnFee[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} + if (snValue){ + const sn_value = isCurrencyEth?snValue?.value:snValue?.value*snValue?.price; + _avgTxnFee[i].sn_value = sn_value; + _avgTxnFee[i].percent_change = sn_value/_avgTxnFee[i].eth_value; + } + }); + setChartData(_avgTxnFee.reverse()); + setChartALoading(false); + break; + case "avgGasUsed": + let _avgGasUsed: IChartData[] = [] + avgGasUsed_ETH.forEach((v,i) => { + let snValue = avgGasUsed_SN.find((val) => val.time === v.time) + // let snValue = avgGasUsed_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + _avgGasUsed[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:v.value/10**9} + if (snValue){ + _avgGasUsed[i].sn_value = snValue.value/10**9; + _avgGasUsed[i].percent_change = (snValue.value/10**9)/_avgGasUsed[i].eth_value; + } + }); + setChartData(_avgGasUsed.reverse()); + setChartBLoading(false); + break; + case "verificationCost": + let _verificationCost: IChartData[] = [] + avgBlockVerificationCost_SN.forEach((v,i) => { + _verificationCost[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:0} + _verificationCost[i].sn_value = isCurrencyEth? v?.value : v?.value*v?.price; + }); + setChartData(_verificationCost.reverse()); + setChartCLoading(false); + break; + default: + let __avgTxnFee: IChartData[] = [] + avgTxnFee_ETH.forEach((v,i) => { + let snValue = avgTxnFee_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + __avgTxnFee[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} + if (snValue){ + const sn_value = isCurrencyEth?snValue?.value:snValue?.value*snValue?.price; + __avgTxnFee[i].sn_value = sn_value; + __avgTxnFee[i].percent_change = sn_value/__avgTxnFee[i].eth_value; + } + }); + setChartData(__avgTxnFee.reverse()); + setChartALoading(false); + } + } + }, [chartDisplay,avgGasUsed_ETH, avgGasUsed_SN, isCurrencyEth]); + + const handleTimeFrame = (period:string) => { + setTimeFrame(period) + } + const chartLoading = chartDisplay === "verificationCost" ? chartCLoading: chartDisplay === "avgGasUsed"? chartBLoading : chartALoading + const totalTxnFeeLatest = snBlock?isCurrencyEth ? Number(ethers.utils.formatEther(snBlock?.avgTxnFee)).toFixed(9) : (Number(ethers.utils.formatEther(snBlock?.avgTxnFee)) * Number(snBlock?.ethPrice)).toFixed(6) : "0.00"; + const verificationCostLatest = snProof?isCurrencyEth ? Number(snProof?.blockVerificationCost).toFixed(9) : (Number(snProof?.blockVerificationCost) * Number(snProof?.ethPrice)).toFixed(6) : "0.00"; + return ( +
+

Block Fee Tracker

+

permissionless layer of Sequencers and Provers ensures that the network will be censorship-resistant

+
+
setChartDisplay('verificationCost')} className={`bg-box text-center rounded-lg p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "verificationCost" ? "border-4 border-sky-900" : ""}`}> + L1 BLOCK VERIFICATION COST +

+ {!snProofLoading ? + <> {verificationCostLatest} {currency.toUpperCase()} + : +
+ +
+ } +

+
+
setChartDisplay('avgTxnFee')} className={`bg-box text-center rounded-lg p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "avgTxnFee" ? "border-4 border-sky-900" : ""}`}> + TOTAL BLOCK FEE +

+ {!snBlockLoading ? + <> {totalTxnFeeLatest} {currency.toUpperCase()} + : +
+ +
+ } +

+
+
setChartDisplay('avgGasUsed')} className={`bg-box text-center rounded-lg p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "avgGasUsed" ? "border-4 border-sky-900" : ""}`}> + TOTAL GAS USED +

+ {!snBlockLoading ? + <> {(snBlock.gasUsedPerblock)/10**9} GWEI + : +
+ +
+ } +

+
+
+
+
+ + + + + {chartDisplay === "avgTxnFee" && } + {chartDisplay === "avgGasUsed" && } + {chartDisplay === "verificationCost" && } + + + + + {chartDisplay === "verificationCost" && snProofData?.detail && snProofData?.detail.map((val:any, key:number) => { + return ( + + + + + + ) + })} + {chartDisplay != "verificationCost" && snBlockData?.detail && snBlockData?.detail.map((val:any, key:number) => { + return ( + + + {chartDisplay === "avgTxnFee" && } + {chartDisplay === "avgGasUsed" && } + + ) + })} + +
BLOCKBLOCK FEE ({currency.toUpperCase()})GAS USED (GWEI)VERIFICATION COST ({currency.toUpperCase()})
#{val?.block_number} + {val?.blockVerificationCost?isCurrencyEth ? Number((val?.blockVerificationCost)).toFixed(9) : (Number((val?.blockVerificationCost))* (Number(val?.ethPrice))).toFixed(6) : "0.00"} +
#{val?.block_number} + {val?.avgTxnFee?isCurrencyEth ? Number(ethers.utils.formatEther(val?.avgTxnFee)).toFixed(9) : (Number(ethers.utils.formatEther(val?.avgTxnFee)) * (Number(val?.ethPrice))).toFixed(6) : "0.00"} + {val?.gasUsedPerblock}
+
+
+
+
+ {chartDisplay === "avgTxnFee" &&

AVERAGE FEE PER BLOCK ({currency.toUpperCase()})

} + {chartDisplay === "avgGasUsed" &&

AVERAGE GAS USED PER BLOCK

} + {chartDisplay === "verificationCost" &&

AVERAGE L1 BLOCK VERIFICATION COST ({currency.toUpperCase()})

} + +
+
+
LATEST BLOCK: {snBlock?.block_number}
+
UPDATED: {lastUpdated} SECONDS AGO
+
UPDATED: {lastUpdated}s AGO
+
+
+
+
CURRENCY
+
setCurrency('eth')} className={`cursor-pointer mx-2 p-1 ${currency==='eth'?"border border-gray-400 bg-sky-900 text-white rounded-sm":""}`}>ETH
+
setCurrency('usd')} className={`cursor-pointer p-1 ${currency==='usd'?"border border-gray-400 bg-sky-900 text-white rounded-sm":""}`}>USD
+
+
+
TIME FRAME
+
handleTimeFrame('4h')} className={`cursor-pointer mx-2 p-1 ${timeFrame==='4h'?"border border-gray-400 bg-sky-900 text-white rounded-sm":""}`}>4h
+
handleTimeFrame('1d')} className={`cursor-pointer p-1 ${timeFrame==='1d'?"border border-gray-400 bg-sky-900 text-white rounded-sm":""}`}>1d
+
+
+
+
+ ); +} diff --git a/src/components/blocks/index.tsx b/src/components/blocks/index.tsx new file mode 100644 index 0000000..fb6c379 --- /dev/null +++ b/src/components/blocks/index.tsx @@ -0,0 +1 @@ +export { default as BlocksPanel } from './BlocksPanel'; \ No newline at end of file diff --git a/src/components/bridge/BridgeChart.tsx b/src/components/bridge/BridgeChart.tsx new file mode 100644 index 0000000..1e5006f --- /dev/null +++ b/src/components/bridge/BridgeChart.tsx @@ -0,0 +1,167 @@ +import { useState, useEffect, useContext } from 'react'; + +import dayjs from 'dayjs'; +import { SpinnerCircular } from "spinners-react"; +import { LineChart, Line, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer } from 'recharts'; +import Numeral from 'numeral'; +import { AppContext } from '../../context/AppContext'; + +export default function BridgeChart(props: { + data:any + isLoading:boolean + chartDisplay:string + currency:string + timeFrame:string +}) { + const { data, isLoading, chartDisplay, currency, timeFrame } = props; + const [chartData, setChartData] = useState([]) + useEffect(() => { + if (data) { + let uniqueTimeFrames:any = []; + const _chartData = data.filter((item:any) => { + const sn_value_exists = chartDisplay === 'deposit' ? true : Object.keys(item).filter((key) => key.includes('sn_value')).length + const isDuplicate = uniqueTimeFrames.includes(item.time); + if (!isDuplicate && sn_value_exists) { + uniqueTimeFrames.push(item.time); + return true; + } + return false; + }) + // console.log(_chartData) + setChartData(_chartData) + } + }, [data]) + + const displayUnit = currency.toUpperCase() + return ( +
+ {!isLoading && data.length ? ( + + + toNiceDate(tick, timeFrame)} + padding={{ left: 20, right: 20 }} + /> + toK(tick)} + interval="preserveEnd" + fontSize={10} + fontFamily='Roboto Mono, monospace' + stroke="#81cefa" + padding={{ top: 20, bottom: 5 }} + /> + } + wrapperStyle={{ top: -70, left: -10, outline: 'none' }} + /> + + {chartDisplay ==='withdraw' && } + + {chartDisplay ==='withdraw' && } + + + ) : ( +
+ +
+ )} +
+ ); +} + +const CustomTooltip = (props:{ active:any, payload:any, label:any, timeFrame:string, displayUnit:string, chartDisplay:string }) => { + const {active, payload, label, timeFrame, displayUnit, chartDisplay} = props; + const { network } = useContext(AppContext) + if (active && payload && payload.length) { + const date = toNiceDateYear(label, timeFrame) + const value1 = toNiceValue(payload[0]?.value, displayUnit, network) + const value2 = chartDisplay === 'deposit' ? '0': toNiceValue(payload[1]?.value, displayUnit, network) + const total = chartDisplay === 'deposit'? '0': toNiceValue(payload[2]?.value, displayUnit, network) + return ( +
+

{date}

+ {chartDisplay === 'deposit' ? ( +

{`Starkgate Deposit : ${value1}`}

+ ) : ( + <> +

{`Initiate Withdraw : ${value1}`}

+

{`Starkgate Withdraw : ${value2}`}

+

{`Total Fee: ${total}`}

+ + )} +
+ ); + } + + return null; + }; + +export const toK = (num:any) => { + return Numeral(num).format('0.[000000]a'); +}; + +export const toK_percent = (num:any) => { + return Numeral(num*100).format('0 a').concat(" %"); +}; + +export const toK_percent_float = (num:any) => { + return Numeral(num*100).format('0.[0000] a').concat(" %"); +}; + +export const toNiceValue = (num:any,unit:string,network:string) => { + if(network === 'goerli'){ + return (num.toFixed(9).concat(" ",unit)); + } else { + return (Numeral(num).format('0.[000000]a').concat(" ",unit)); + } +}; + +export const toNiceDateYear = (date:any, timeFrame:string) => { + let x = timeFrame === '1d' ? dayjs(date).format('MMM DD, YYYY'): dayjs(date).format('MMM DD, YYYY h:mm A'); + return x; +}; + +export const toNiceDate = (date:any, timeFrame:string) => { + let x = timeFrame === '1d' ? dayjs(date).format('DD/MM'): dayjs(date).format('DD/MM HH:mm'); + return x; +}; \ No newline at end of file diff --git a/src/components/bridge/BridgePanel.tsx b/src/components/bridge/BridgePanel.tsx new file mode 100644 index 0000000..b595480 --- /dev/null +++ b/src/components/bridge/BridgePanel.tsx @@ -0,0 +1,222 @@ +import { useState, useEffect, useContext } from 'react'; +import dayjs from 'dayjs'; +import { SpinnerCircular } from "spinners-react"; +import BridgeChart from './BridgeChart'; +import { PriceContext } from '../../context/PriceContext'; +import { AppContext } from '../../context/AppContext'; +import { useEthFeeEstimateQuery, useEthFeeEstimateQuery4h, useEthFeeEstimateQuery1d } from '../../queries/ethereum'; + +interface IData { + time: string; + value: number; + price: number; +} + +interface IChartData { + time: string; + sn_value?: number; + eth_value: number; + total_value?: number; +} + +export default function BridgePanel(props: { + snDetailLoading:boolean + snDetailData:any + snBlockLoading: boolean + snBlockData:any + ethDetailLoading:boolean + ethDetailData:any + timeFrame:string + setTimeFrame:any +}) { + const {snDetailLoading, snBlockLoading, ethDetailLoading, snDetailData, snBlockData, ethDetailData, timeFrame, setTimeFrame } = props; + const { ethPrice } = useContext(PriceContext) + const { network } = useContext(AppContext) + + const {refetch, data: ethFeeEstimate} = useEthFeeEstimateQuery(network, '4h', '1', '0'); + const {isLoading:ethFeeEstimateLoading_4h, data: ethFeeEstimateData_4h} = useEthFeeEstimateQuery4h('goerli'); + const {isLoading:ethFeeEstimateLoading_1d, data: ethFeeEstimateData_1d} = useEthFeeEstimateQuery1d('goerli'); + const ethFeeEstimateData = timeFrame === '4h' ? ethFeeEstimateData_4h : ethFeeEstimateData_1d + const ethFeeEstimateLoading = timeFrame === '4h' ? ethFeeEstimateLoading_4h : ethFeeEstimateLoading_1d + + const [chartLoading, setChartLoading] = useState(true); + const [lastUpdated, setLastUpdated] = useState(0); + const [depositFeeLatest, setDepositFeeLatest] = useState('0.001791952') + const [withdrawFeeLatest, setWithdrawFeeLatest] = useState('') + const [initiateWithdrawFeeLatest, setInitiateWithdrawFeeLatest] = useState('') + const [totalWithdrawFeeLatest, setTotalWithdrawFeeLatest] = useState('') + const [currency, setCurrency] = useState('eth'); + const [depositChartData, setDepositChartData] = useState([]); + const [withdrawChartData, setWithdrawChartData] = useState([]); + + const [initiateWithdrawFeeData_SN, setInitiateWithdrawFeeData_SN] = useState([]); + + const [bridgeDepositFeeData_ETH, setBridgeDepositFeeData_ETH] = useState([]); + const [bridgeWithdrawFeeData_ETH, setBridgeWithdrawFeeData_ETH] = useState([]); + const isCurrencyEth: boolean = currency === 'eth'? true : false; + const snBlock = snBlockData?.detail[0]; + + useEffect(() => { + refetch(); + }, [network]) + + useEffect(() => { + if (ethFeeEstimate) { + const _depositFee = isCurrencyEth ? ethFeeEstimate[0].feeEstimate?.bridgeDepositFee.toFixed(9) : (Number(ethFeeEstimate[0].feeEstimate?.bridgeDepositFee) * ethPrice).toFixed(9); + const _withdrawFee = isCurrencyEth ? ethFeeEstimate[0].feeEstimate?.bridgeWithdrawFee.toFixed(9) : (Number(ethFeeEstimate[0].feeEstimate?.bridgeWithdrawFee) * ethPrice).toFixed(9); + setDepositFeeLatest(_depositFee); + setWithdrawFeeLatest(_withdrawFee); + } + }, [ethFeeEstimate, isCurrencyEth]) + + useEffect(() => { + const interval = setInterval(() => { + setLastUpdated(dayjs().diff(snBlockData?.lastUpdated, 'seconds')); + }, 1000); + + const getFeeEstimate = async() =>{ + const initiateWithdrawFee = snBlock?.feeEstimate?.inititateWithdrawFee.toFixed(9) + // console.log(initiateWithdrawFee) + // const x = await getBridgeFee(network) + // const initiateWithdrawFee = x.initiateWithdrawFee; + setInitiateWithdrawFeeLatest(initiateWithdrawFee); + } + getFeeEstimate(); + refetch(); + return () => clearInterval(interval); + }, [snBlockData]); + + useEffect(() => { + if (initiateWithdrawFeeLatest && withdrawFeeLatest) { + const _initiateWithdrawFee = isCurrencyEth ? Number(initiateWithdrawFeeLatest) : (Number(initiateWithdrawFeeLatest) * ethPrice); + const _totalWithdraw: number = Number(_initiateWithdrawFee) + Number(withdrawFeeLatest) + // console.log("_initiateWithdrawFee = ", _initiateWithdrawFee) + // console.log("withdrawFeeLatest = ", withdrawFeeLatest) + // console.log("_totalWithdraw = ", _totalWithdraw) + setTotalWithdrawFeeLatest(_totalWithdraw.toFixed(9)) + + + } + }, [initiateWithdrawFeeLatest, withdrawFeeLatest, isCurrencyEth]); + + useEffect(() => { + setChartLoading(true); + }, [network]); + + useEffect(() => { + if(snDetailData){ + const _initiateWithdrawFeeData: IData[] = snDetailData.filter((item:any) => item.timestamp > 1667634995).map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.inititateWithdrawFee, price:item?.ethPrice})); + setInitiateWithdrawFeeData_SN(_initiateWithdrawFeeData) + } + }, [snDetailData]); + + useEffect(() => { + if (network != 'goerli') { + if(ethDetailData){ + const _bridgeDepositFeeData: IData[] = ethDetailData.filter((item:any) => item.timestamp > 1667634995).map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.bridgeDepositFee, price:item?.ethPrice})); + const _bridgeWithdrawFeeData: IData[] = ethDetailData.filter((item:any) => item.timestamp > 1667634995).map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.bridgeWithdrawFee, price:item?.ethPrice})); + setBridgeDepositFeeData_ETH(_bridgeDepositFeeData); + setBridgeWithdrawFeeData_ETH(_bridgeWithdrawFeeData); + } + } else { + if(ethFeeEstimateData){ + const _bridgeDepositFeeData: IData[] = ethFeeEstimateData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.bridgeDepositFee, price:item?.ethPrice})); + const _bridgeWithdrawFeeData: IData[] = ethFeeEstimateData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.bridgeWithdrawFee, price:item?.ethPrice})); + setBridgeDepositFeeData_ETH(_bridgeDepositFeeData); + setBridgeWithdrawFeeData_ETH(_bridgeWithdrawFeeData); + } + } + }, [ethDetailData, ethFeeEstimateData, network]); + + useEffect(() => { + if (bridgeDepositFeeData_ETH){ + let _depositData: IChartData[] = [] + bridgeDepositFeeData_ETH.forEach((v,i) => { + _depositData[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} + }); + setDepositChartData(_depositData.reverse()); + } + + }, [bridgeDepositFeeData_ETH, isCurrencyEth]); + + useEffect(() => { + if (bridgeWithdrawFeeData_ETH){ + let _withdrawData: IChartData[] = [] + bridgeWithdrawFeeData_ETH.forEach((v,i) => { + let snValue = initiateWithdrawFeeData_SN.find((val) => val.time === v.time) + // let snValue = initiateWithdrawFeeData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + _withdrawData[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} + if (snValue){ + const sn_value = isCurrencyEth?snValue?.value:snValue?.value*snValue?.price; + _withdrawData[i].sn_value = sn_value; + _withdrawData[i].total_value = sn_value+_withdrawData[i].eth_value; + } + }); + setWithdrawChartData(_withdrawData.reverse()); + setChartLoading(false); + } + + }, [bridgeWithdrawFeeData_ETH, initiateWithdrawFeeData_SN, isCurrencyEth]); + + const handleTimeFrame = (period:string) => { + setTimeFrame(period) + } + + return ( +
+

Bridge Fee Tracker

+

preserving the security of L1 Ethereum by producing STARK proofs off-chain

+
+
+ STARKGATE DEPOSIT FEE +

+ {!snBlockLoading && !chartLoading? + <> {depositFeeLatest} {currency.toUpperCase()} + : +
+ +
+ } +

+
+
+ STARKGATE WITHDRAWAL FEE (TOTAL) +

+ {!snBlockLoading && !chartLoading? + <> {totalWithdrawFeeLatest} {currency.toUpperCase()} + : +
+ +
+ } +

+
+
+

AVERAGE DEPOSIT FEE ({currency.toUpperCase()})

+ +
+
+

AVERAGE WITHDRAWAL FEE ({currency.toUpperCase()})

+ +
+
+
UPDATED: {lastUpdated} SECONDS AGO
+
UPDATED: {lastUpdated}s AGO
+
+
+
+
CURRENCY
+
setCurrency('eth')} className={`cursor-pointer mx-2 p-1 ${currency==='eth'?"border border-gray-400 bg-sky-900 text-white rounded-sm":""}`}>ETH
+
setCurrency('usd')} className={`cursor-pointer p-1 ${currency==='usd'?"border border-gray-400 bg-sky-900 text-white rounded-sm":""}`}>USD
+
+
+
TIME FRAME
+
handleTimeFrame('4h')} className={`cursor-pointer mx-2 p-1 ${timeFrame==='4h'?"border border-gray-400 bg-sky-900 text-white rounded-sm":""}`}>4h
+
handleTimeFrame('1d')} className={`cursor-pointer p-1 ${timeFrame==='1d'?"border border-gray-400 bg-sky-900 text-white rounded-sm":""}`}>1d
+
+
+
+
+ ); +} + diff --git a/src/components/bridge/index.tsx b/src/components/bridge/index.tsx new file mode 100644 index 0000000..8b57c56 --- /dev/null +++ b/src/components/bridge/index.tsx @@ -0,0 +1 @@ +export { default as BridgePanel } from './BridgePanel'; \ No newline at end of file diff --git a/src/components/fee/FeeChart.tsx b/src/components/fee/FeeChart.tsx index f36dc91..ade2339 100644 --- a/src/components/fee/FeeChart.tsx +++ b/src/components/fee/FeeChart.tsx @@ -1,9 +1,11 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useContext } from 'react'; import dayjs from 'dayjs'; import { SpinnerCircular } from "spinners-react"; -import { LineChart, Line, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer } from 'recharts'; +import { LineChart, Line, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, Symbols, Surface } from 'recharts'; import Numeral from 'numeral'; +import { useMediaQuery } from 'react-responsive' +import { AppContext } from '../../context/AppContext'; export default function FeeChart(props: { data:any @@ -13,13 +15,16 @@ export default function FeeChart(props: { timeFrame:string }) { const { data, isLoading, chartDisplay, currency, timeFrame } = props; - const [chartData, setChartData] = useState([]) + const [chartData, setChartData] = useState([]) + const [legendState, setLegendState] = useState([true,true,true]) + useEffect(() => { if (data) { let uniqueTimeFrames:any = []; const _chartData = data.filter((item:any) => { + const sn_value_exists = Object.keys(item).filter((key) => key.includes('sn_value')).length const isDuplicate = uniqueTimeFrames.includes(item.time); - if (!isDuplicate) { + if (!isDuplicate && sn_value_exists) { uniqueTimeFrames.push(item.time); return true; } @@ -28,8 +33,46 @@ export default function FeeChart(props: { setChartData(_chartData) } }, [data]) + + const displayUnit = currency.toUpperCase() + const onlyY0 = legendState.filter(v => v).length === 1; + + const handleClick = (index:number) => { + if (legendState) { + const _legendState = legendState.map(a => {return a}); + _legendState[index] = !_legendState[index] + + if ( _legendState.includes(true)) { + setLegendState(_legendState); + } + } + }; + + const renderLegend = (props:any) => { + const { payload } = props; + return ( +
+ { + payload.map((entry:any, index:any) => ( +
handleClick(index)} key={`item-${index}`} className={`flex justify-center items-center text-[${entry.color}] cursor-pointer hover:bg-gray-900 ${legendState[index]?"":'opacity-25'} rounded-md mx-1 px-1`}> + + + + {entry.value} +
+ )) + } +
+ ); + } + const isXsScreen = useMediaQuery({ minWidth: 600 }) + const swapFeeLedgendSn = !isXsScreen ? "My Swap":"on Starknet (My Swap)" + const swapFeeLedgendEth = !isXsScreen ? "UniswapV2":"on Ethereum (UniswapV2)" + const mintFeeLedgendSn = !isXsScreen ? "Mint Square":"on Starknet (Mint Square)" + const mintFeeLedgendEth = !isXsScreen ? "Rarible":"on Ethereum (Rarible)" + const snLegendName = chartDisplay === "swapFee" ? swapFeeLedgendSn :chartDisplay === "nftMintFee" ? mintFeeLedgendSn: "on Starknet"; + const ethLegendName = chartDisplay === "swapFee" ? swapFeeLedgendEth :chartDisplay === "nftMintFee" ? mintFeeLedgendEth: "on Ethereum"; - const displayUnit = chartDisplay === "avgGasUsed" ? "Gwei" : currency.toUpperCase() return (
{!isLoading && data.length ? ( @@ -39,64 +82,65 @@ export default function FeeChart(props: { width={400} data={chartData} margin={{ - top: 5, - right: 5, - left: 5, - bottom: 5, + top: 5, + right: 5, + left: 5, + bottom: 5, }} > toNiceDate(tick, timeFrame)} - padding={{ left: 20, right: 20 }} + dataKey="time" + tickLine={false} + axisLine={true} + interval="preserveEnd" + fontSize={10} + fontFamily='Roboto Mono, monospace' + stroke="#81cefa" + label="time" + tickFormatter={tick => toNiceDate(tick, timeFrame)} + padding={{ left: 20, right: 20 }} /> toK(tick)} - interval="preserveEnd" - fontSize={10} - fontFamily='Roboto Mono, monospace' - stroke="#81cefa" - padding={{ top: 20, bottom: 5 }} + yAxisId={0} + dataKey= {!legendState[1] && legendState[0] ? "sn_value" : !legendState[1] ? "percent_change" : "eth_value" } + axisLine={true} + tickLine={false} + type="number" + orientation="right" + tickFormatter={tick => onlyY0 && legendState[2] ? toK_percent(tick):toK(tick)} + interval="preserveEnd" + fontSize={10} + fontFamily='Roboto Mono, monospace' + stroke={!legendState[1] && legendState[0] ? "#fb7185" : !legendState[1] ? "#b9b72b" : "#81cefa"} + padding={{ top: 20, bottom: 5 }} /> toK_percent(tick)} - interval="preserveStartEnd" - fontSize={10} - fontFamily='Roboto Mono, monospace' - stroke="#81cefa" - padding={{ top: 20, bottom: 5 }} + yAxisId={1} + hide= {onlyY0} + dataKey={legendState[2]?"percent_change":"sn_value"} + domain={legendState[2]?[0, 1]:[0, 'auto']} + axisLine={true} + tickLine={false} + type="number" + orientation="left" + tickFormatter={tick => legendState[2]? toK_percent(tick): toK(tick)} + interval="preserveStartEnd" + fontSize={10} + fontFamily='Roboto Mono, monospace' + stroke={legendState[2]?"#b9b72b":"#fb7185"} + padding={{ top: 20, bottom: 5 }} /> } - wrapperStyle={{ top: -70, left: -10, outline: 'none' }} + cursor={true} + active={false} + //@ts-ignore + content={} + wrapperStyle={{ top: -70, left: -10, outline: 'none' }} /> - - - - + + + + ) : ( @@ -114,13 +158,14 @@ export default function FeeChart(props: { ); } -const CustomTooltip = (props:{ active:any, payload:any, label:any, timeFrame:string, displayUnit:string }) => { - const {active, payload, label, timeFrame, displayUnit} = props; +const CustomTooltip = (props:{ active:any, payload:any, label:any, timeFrame:string, displayUnit:string, snLegendName:string, ethLegendName:string, legendState:boolean[] }) => { + const {active, payload, label, timeFrame, displayUnit, legendState, ethLegendName, snLegendName} = props; + const { network } = useContext(AppContext) if (active && payload && payload.length) { const date = toNiceDateYear(label, timeFrame) - const snValue = toNiceValue(payload[0].value, displayUnit) - const ethValue = toNiceValue(payload[1].value, displayUnit) - const l2vsl1 = toK_percent_float(payload[2].value) + const snValue = toNiceValue(payload[0]?.value, displayUnit, network) + const ethValue = toNiceValue(payload[legendState[0] && legendState[1] ? 1 : 0]?.value, displayUnit, network) + const l2vsl1 = toK_percent_float(payload[legendState[0] && legendState[1] ? 2 : !legendState[0] && !legendState[1] ? 0 : 1]?.value) return (

{date}

-

{`on Starknet : ${snValue}`}

-

{`on Ethereum : ${ethValue}`}

-

{`L2 vs L1 : ${l2vsl1}`}

+ {legendState[0] &&

{`${snLegendName} : ${snValue}`}

} + {legendState[1] &&

{`${ethLegendName} : ${ethValue}`}

} + {legendState[2] &&

{`L2 vs L1 : ${l2vsl1}`}

}
); } @@ -150,11 +195,15 @@ export const toK_percent = (num:any) => { }; export const toK_percent_float = (num:any) => { - return Numeral(num*100).format('0.[00] a').concat(" %"); + return Numeral(num*100).format('0.[0000] a').concat(" %"); }; -export const toNiceValue = (num:any,unit:string) => { - return (Numeral(num).format('0.[000000]a').concat(" ",unit)); +export const toNiceValue = (num:any,unit:string,network:string) => { + if(network === 'goerli'){ + return (num.toFixed(9).concat(" ",unit)); + } else { + return (Numeral(num).format('0.[000000]a').concat(" ",unit)); + } }; export const toNiceDateYear = (date:any, timeFrame:string) => { diff --git a/src/components/fee/FeePanel.tsx b/src/components/fee/FeePanel.tsx index c6e7803..2f8f405 100644 --- a/src/components/fee/FeePanel.tsx +++ b/src/components/fee/FeePanel.tsx @@ -1,10 +1,9 @@ import { useState, useEffect, useContext } from 'react'; import dayjs from 'dayjs'; -import {ethers} from 'ethers'; import { SpinnerCircular } from "spinners-react"; -import { getEthTransferFee } from "../../services/stark"; import FeeChart from './FeeChart'; import { PriceContext } from '../../context/PriceContext'; +import { AppContext } from '../../context/AppContext'; interface IData { time: string; @@ -31,20 +30,23 @@ export default function FeePanel(props: { }) { const {snDetailLoading, snBlockLoading, ethDetailLoading, snDetailData, snBlockData, ethDetailData, timeFrame, setTimeFrame } = props; const { ethPrice } = useContext(PriceContext) + const { network } = useContext(AppContext) const [chartLoading, setChartLoading] = useState(false); const [lastUpdated, setLastUpdated] = useState(0); - const [feeEstimate, setFeeEstimate] = useState<{ ethTransferFee:string, erc20TransferFee:string }>({ ethTransferFee:'0.000233541', erc20TransferFee:'0.000233495' }); + const [feeEstimate, setFeeEstimate] = useState<{ ethTransferFee:string, erc20TransferFee:string, nftMintFee:string, swapFee:string }>({ ethTransferFee:'0.000081779', erc20TransferFee:'0.000081757', nftMintFee:'0.000160568', swapFee:'0.000166792' }); const [chartDisplay, setChartDisplay] = useState('usdcTransferFee'); const [currency, setCurrency] = useState('eth'); const [chartData, setChartData] = useState([]); + const [ethTransferData_SN, setEthTransferData_SN] = useState([]); const [usdcTransferData_SN, setUsdcTransferData_SN] = useState([]); - const [avgTxnFee_SN, setAvgTxnFee_SN] = useState([]); - const [avgGasUsed_SN, setAvgGasUsed_SN] = useState([]); + const [swapFeeData_SN, setSwapFeeData_SN] = useState([]); + const [nftMintFeeData_SN, setNftMintFeeData_SN] = useState([]); + const [ethTransferData_ETH, setEthTransferData_ETH] = useState([]); const [usdcTransferData_ETH, setUsdcTransferData_ETH] = useState([]); - const [avgTxnFee_ETH, setAvgTxnFee_ETH] = useState([]); - const [avgGasUsed_ETH, setAvgGasUsed_ETH] = useState([]); + const [swapFeeData_ETH, setSwapFeeData_ETH] = useState([]); + const [nftMintFeeData_ETH, setNftMintFeeData_ETH] = useState([]); const snBlock = snBlockData?.detail[0]; @@ -55,23 +57,42 @@ export default function FeePanel(props: { }, 1000); const getFeeEstimate = async() =>{ - const x = await getEthTransferFee() - setFeeEstimate(x); + // const x = await getEthTransferFee(network) + let _feeEstimate = snBlock?.feeEstimate + if (_feeEstimate) { + _feeEstimate.ethTransferFee = _feeEstimate.ethTransferFee.toFixed(9) + _feeEstimate.erc20TransferFee = _feeEstimate.erc20TransferFee.toFixed(9) + _feeEstimate.swapFee = _feeEstimate.swapFee.toFixed(9) + _feeEstimate.nftMintFee = _feeEstimate.nftMintFee.toFixed(9) + setFeeEstimate(_feeEstimate); + } } getFeeEstimate(); return () => clearInterval(interval); }, [snBlockData]); + // useEffect(() => { + // if (feeEstimate) { + // setChartLoading(false); + // } + // }, [feeEstimate]); + + useEffect(() => { + if (ethTransferData_SN.length && usdcTransferData_SN.length) { + setChartLoading(true); + } + }, [network]); + useEffect(() => { if(snDetailData){ const _ethTransferData: IData[] = snDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.ethTransferFee, price:item?.ethPrice})); const _usdcTransferData: IData[] = snDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.erc20TransferFee, price:item?.ethPrice})); - const _avgTxnFee: IData[] = snDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:Number(ethers?.utils.formatEther(item.avgTxnFee)), price:item?.ethPrice})); - const _avgGasUsed: IData[] = snDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.gasUsedPerblock, price:item?.ethPrice})); + const _swapFeeData: IData[] = snDetailData.filter((item:any) => item.timestamp > 1667634995).map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.swapFee, price:item?.ethPrice})); + const _nftMintFeeData: IData[] = snDetailData.filter((item:any) => item.timestamp > 1667634995).map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.nftMintFee, price:item?.ethPrice})); setEthTransferData_SN(_ethTransferData); setUsdcTransferData_SN(_usdcTransferData); - setAvgTxnFee_SN(_avgTxnFee); - setAvgGasUsed_SN(_avgGasUsed); + setSwapFeeData_SN(_swapFeeData); + setNftMintFeeData_SN(_nftMintFeeData); } }, [snDetailData]); @@ -79,24 +100,24 @@ export default function FeePanel(props: { if(ethDetailData){ const _ethTransferData: IData[] = ethDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.ethTransferFee, price:item?.ethPrice})); const _usdcTransferData: IData[] = ethDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.erc20TransferFee, price:item?.ethPrice})); - const _avgTxnFee: IData[] = ethDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:Number(ethers?.utils.formatEther(item.avgTxnFee)), price:item?.ethPrice})); - const _avgGasUsed: IData[] = ethDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.gasUsedPerblock, price:item?.ethPrice})); + const _swapFeeData: IData[] = ethDetailData.filter((item:any) => item.timestamp > 1667634995).map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.swapFee, price:item?.ethPrice})); + const _nftMintFeeData: IData[] = ethDetailData.filter((item:any) => item.timestamp > 1667634995).map((item: any) => ({time:dayjs(item?.timestamp*1000).format('MMM DD, YYYY HH:00'), value:item?.feeEstimate?.nftMintFee, price:item?.ethPrice})); setEthTransferData_ETH(_ethTransferData); setUsdcTransferData_ETH(_usdcTransferData); - setAvgTxnFee_ETH(_avgTxnFee); - setAvgGasUsed_ETH(_avgGasUsed); + setSwapFeeData_ETH(_swapFeeData); + setNftMintFeeData_ETH(_nftMintFeeData); } }, [ethDetailData]); useEffect(() => { if(chartDisplay){ - setChartLoading(true); + // setChartLoading(true); switch(chartDisplay) { case "ethTransferFee": let _ethTransferData: IChartData[] = [] ethTransferData_ETH.forEach((v,i) => { - // let snValue = ethTransferData_SN.find((val) => val.time === v.time) - let snValue = ethTransferData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + let snValue = ethTransferData_SN.find((val) => val.time === v.time) + // let snValue = ethTransferData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) _ethTransferData[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} if (snValue){ const sn_value = isCurrencyEth?snValue?.value:snValue?.value*snValue?.price; @@ -109,8 +130,8 @@ export default function FeePanel(props: { case "usdcTransferFee": let _usdcTransferData: IChartData[] = [] usdcTransferData_ETH.forEach((v,i) => { - // let snValue = usdcTransferData_SN.find((val) => val.time === v.time) - let snValue = usdcTransferData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + let snValue = usdcTransferData_SN.find((val) => val.time === v.time) + // let snValue = usdcTransferData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) _usdcTransferData[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} if (snValue){ const sn_value = isCurrencyEth?snValue?.value:snValue?.value*snValue?.price; @@ -120,109 +141,113 @@ export default function FeePanel(props: { }); setChartData(_usdcTransferData.reverse()); break; - case "avgTxnFee": - let _avgTxnFee: IChartData[] = [] - avgTxnFee_ETH.forEach((v,i) => { - // let snValue = avgTxnFee_SN.find((val) => val.time === v.time) - let snValue = avgTxnFee_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) - _avgTxnFee[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} + case "swapFee": + let _swapFeeData: IChartData[] = [] + swapFeeData_ETH.forEach((v,i) => { + let snValue = swapFeeData_SN.find((val) => val.time === v.time) + // let snValue = swapFeeData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + _swapFeeData[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} if (snValue){ const sn_value = isCurrencyEth?snValue?.value:snValue?.value*snValue?.price; - _avgTxnFee[i].sn_value = sn_value; - _avgTxnFee[i].percent_change = sn_value/_avgTxnFee[i].eth_value; + _swapFeeData[i].sn_value = sn_value; + _swapFeeData[i].percent_change = sn_value/_swapFeeData[i].eth_value; } }); - setChartData(_avgTxnFee.reverse()); + setChartData(_swapFeeData.reverse()); break; - case "avgGasUsed": - let _avgGasUsed: IChartData[] = [] - avgGasUsed_ETH.forEach((v,i) => { - // let snValue = avgGasUsed_SN.find((val) => val.time === v.time) - let snValue = avgGasUsed_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) - _avgGasUsed[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:v.value/10**9, sn_value:avgGasUsed_SN[i].value/10**9} + case "nftMintFee": + let _nftMintFeeData: IChartData[] = [] + nftMintFeeData_ETH.forEach((v,i) => { + let snValue = nftMintFeeData_SN.find((val) => val.time === v.time) + // let snValue = nftMintFeeData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + _nftMintFeeData[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} if (snValue){ - _avgGasUsed[i].sn_value = snValue.value/10**9; - _avgGasUsed[i].percent_change = (snValue.value/10**9)/_avgGasUsed[i].eth_value; + const sn_value = isCurrencyEth?snValue?.value:snValue?.value*snValue?.price; + _nftMintFeeData[i].sn_value = sn_value; + _nftMintFeeData[i].percent_change = sn_value/_nftMintFeeData[i].eth_value; } }); - setChartData(_avgGasUsed.reverse()); + setChartData(_nftMintFeeData.reverse()); break; default: let __ethTransferData: IChartData[] = [] ethTransferData_ETH.forEach((v,i) => { - // let snValue = ethTransferData_SN.find((val) => val.time === v.time) - let snValue = ethTransferData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + let snValue = ethTransferData_SN.find((val) => val.time === v.time) + // let snValue = ethTransferData_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') >= -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) __ethTransferData[i] = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:isCurrencyEth?v.value:v.value*v.price} if (snValue){ const sn_value = isCurrencyEth?snValue?.value:snValue?.value*snValue?.price; - _ethTransferData[i].sn_value = sn_value; - _ethTransferData[i].percent_change = sn_value/_ethTransferData[i].eth_value; + __ethTransferData[i].sn_value = sn_value; + __ethTransferData[i].percent_change = sn_value/__ethTransferData[i].eth_value; } }); setChartData(__ethTransferData.reverse()); } setChartLoading(false); } - }, [chartDisplay,avgGasUsed_ETH, avgGasUsed_SN, isCurrencyEth]); + }, [chartDisplay,nftMintFeeData_ETH, nftMintFeeData_SN, isCurrencyEth]); const handleTimeFrame = (period:string) => { setTimeFrame(period) } - const ethTranferFeeLatest = isCurrencyEth ? feeEstimate?.ethTransferFee : (Number(feeEstimate?.ethTransferFee) * ethPrice).toFixed(4); - const erc20TransferFeeLatest = isCurrencyEth ? feeEstimate?.erc20TransferFee : (Number(feeEstimate?.erc20TransferFee) * ethPrice).toFixed(4); - const totalTxnFeeLatest = snBlock?isCurrencyEth ? Number(ethers.utils.formatEther(snBlock?.avgTxnFee)).toFixed(9) : (Number(ethers.utils.formatEther(snBlock?.avgTxnFee)) * ethPrice).toFixed(4) : "0.00"; + const ethTranferFeeLatest = isCurrencyEth ? feeEstimate?.ethTransferFee : (Number(feeEstimate?.ethTransferFee) * ethPrice).toFixed(6); + const erc20TransferFeeLatest = isCurrencyEth ? feeEstimate?.erc20TransferFee : (Number(feeEstimate?.erc20TransferFee) * ethPrice).toFixed(6); + const swapFeeFeeLatest = isCurrencyEth ? feeEstimate?.swapFee : (Number(feeEstimate?.swapFee) * ethPrice).toFixed(6); //fix + const nftMintFeeLatest = isCurrencyEth ? feeEstimate?.nftMintFee : (Number(feeEstimate?.nftMintFee) * ethPrice).toFixed(6); //fix return (

Fee Tracker

save upto 10x on asset transfer fees

-
setChartDisplay('usdcTransferFee')} className={`bg-box rounded-lg p-3 px-5 2xl:p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "usdcTransferFee" ? "border-4 border-sky-900" : ""}`}> +
setChartDisplay('usdcTransferFee')} className={`bg-box rounded-lg p-3 px-4 2xl:p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "usdcTransferFee" ? "border-4 border-sky-900" : ""}`}> USDC TRANSFER FEE (ERC20)

- {!snBlockLoading ? + {!snBlockLoading? <> {erc20TransferFeeLatest} {currency.toUpperCase()} : -
- +
+
}

-
setChartDisplay('ethTransferFee')} className={`bg-box rounded-lg p-3 px-5 2xl:p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "ethTransferFee" ? "border-4 border-sky-900" : ""}`}> +
setChartDisplay('ethTransferFee')} className={`bg-box rounded-lg p-3 px-4 2xl:p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "ethTransferFee" ? "border-4 border-sky-900" : ""}`}> ETH TRANSFER FEE

{!snBlockLoading ? <> {ethTranferFeeLatest} {currency.toUpperCase()} : -
- +
+
}

-
setChartDisplay('avgTxnFee')} className={`bg-box rounded-lg p-3 px-5 2xl:p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "avgTxnFee" ? "border-4 border-sky-900" : ""}`}> - TOTAL TXN FEE +
setChartDisplay('swapFee')} className={`bg-box rounded-lg p-3 px-4 2xl:p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "swapFee" ? "border-4 border-sky-900" : ""}`}> + SWAP FEE + (MY SWAP)

{!snBlockLoading ? - <> {totalTxnFeeLatest} {currency.toUpperCase()} + <> {swapFeeFeeLatest} {currency.toUpperCase()} : -
- +
+
}

-
setChartDisplay('avgGasUsed')} className={`bg-box rounded-lg p-3 px-5 2xl:p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "avgGasUsed" ? "border-4 border-sky-900" : ""}`}> - TOTAL GAS USED +
setChartDisplay('nftMintFee')} className={`bg-box rounded-lg p-3 px-4 2xl:p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "nftMintFee" ? "border-4 border-sky-900" : ""}`}> + NFT MINT FEE + (MINT SQUARE)

{!snBlockLoading ? - <> {(snBlock.gasUsedPerblock)/10**9} Gwei + <> {nftMintFeeLatest} {currency.toUpperCase()} : -
- +
+
}

@@ -230,16 +255,16 @@ export default function FeePanel(props: {
- {chartDisplay === 'ethTransferFee' &&

AVERAGE ETH TRANSFER FEE ({currency.toUpperCase()})

} - {chartDisplay === 'usdcTransferFee' &&

AVERAGE USDC TRANSFER FEE ({currency.toUpperCase()})

} - {chartDisplay === 'avgTxnFee' &&

AVERAGE TRANSACTION FEE PER BLOCK ({currency.toUpperCase()})

} - {chartDisplay === 'avgGasUsed' &&

AVERAGE GAS USED PER BLOCK (Gwei)

} - + {chartDisplay === 'ethTransferFee' &&

AVERAGE ETH TRANSFER FEE ({currency.toUpperCase()})

} + {chartDisplay === 'usdcTransferFee' &&

AVERAGE USDC TRANSFER FEE ({currency.toUpperCase()})

} + {chartDisplay === 'swapFee' &&

AVERAGE SWAP FEE ({currency.toUpperCase()})

} + {chartDisplay === 'nftMintFee' &&

AVERAGE NFT MINT FEE ({currency.toUpperCase()})

} +
LATEST BLOCK: {snBlock?.block_number}
-
UPDATED: {lastUpdated} SECONDS AGO
-
UPDATED: {lastUpdated}s AGO
+
UPDATED: {lastUpdated} SECONDS AGO
+
UPDATED: {lastUpdated}s AGO
diff --git a/src/components/footer/Footer.tsx b/src/components/footer/Footer.tsx index a0be6f9..9f4d90d 100644 --- a/src/components/footer/Footer.tsx +++ b/src/components/footer/Footer.tsx @@ -19,7 +19,7 @@ export default function Footer() {; Juno
- Wrap + Warp
@@ -41,7 +41,10 @@ export default function Footer() {;

LEGAL

- Disclaimer + Terms of Use +
+
+ *Disclaimer
diff --git a/src/components/latency/LatencyChart.tsx b/src/components/latency/LatencyChart.tsx index f0f31d9..db70d1e 100644 --- a/src/components/latency/LatencyChart.tsx +++ b/src/components/latency/LatencyChart.tsx @@ -19,20 +19,24 @@ export default function LatencyChart(props: { if (data) { let uniqueData:any = []; const _chartData = data.filter((item:any) => { + const sn_value_exists = Object.keys(item).filter((key) => key.includes('sn_value')).length && item?.sn_value != null + const normalDensityZx_exists = Object.keys(item).filter((key) => key.includes('normalDensityZx')).length && item?.value != null && item?.normalDensityZx != null + // const value_exists = item.value? const isDuplicate = uniqueData.includes(isLatencyChart?item.time:item.value); - if (!isDuplicate) { + if (!isDuplicate && (sn_value_exists || normalDensityZx_exists)) { uniqueData.push(item.time); return true; } return false; }) + // console.log(_chartData) setChartData(_chartData) } }, [data]) const rightMargin = isLatencyChart ? 5 : 40; return ( -
+
{!isLoading && data.length ? ( } diff --git a/src/components/latency/LatencyPanel.tsx b/src/components/latency/LatencyPanel.tsx index dc3dd3c..c847b7a 100644 --- a/src/components/latency/LatencyPanel.tsx +++ b/src/components/latency/LatencyPanel.tsx @@ -1,9 +1,10 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useContext } from 'react'; import dayjs from 'dayjs'; import { SpinnerCircular } from "spinners-react"; -import LatencyChart from './LatencyChart'; import Numeral from 'numeral'; +import LatencyChart from './LatencyChart'; +import { AppContext } from '../../context/AppContext'; interface IData { time: string; @@ -22,13 +23,17 @@ export default function LatencyPanel(props: { snBlockLoading: boolean snBlockData:any ethDetailLoading:boolean + snProofData:any + snProofLoading:boolean ethDetailData:any timeFrame:string setTimeFrame:any }) { - const {snDetailLoading, snBlockLoading, ethDetailLoading, snDetailData, snBlockData, ethDetailData, timeFrame, setTimeFrame } = props; + const {snDetailLoading, snBlockLoading, ethDetailLoading, snProofLoading, snDetailData, snBlockData, snProofData, ethDetailData, timeFrame, setTimeFrame } = props; + const { network } = useContext(AppContext) const [chartLoading, setChartLoading] = useState(false); const [lastUpdated, setLastUpdated] = useState(0); + const [chartDisplay, setChartDisplay] = useState('actualProof'); const [chartData, setChartData] = useState([]); const [avgBlockLatency_SN, setAvgBlockLatency_SN] = useState([]); @@ -37,6 +42,7 @@ export default function LatencyPanel(props: { const [variance, setVariance] = useState(0) const snBlock = snBlockData?.detail[0]; + const snProof = snProofData?.detail[0]; useEffect(() => { const interval = setInterval(() => { @@ -49,10 +55,11 @@ export default function LatencyPanel(props: { useEffect(() => { if(snDetailData){ const timeFormat = timeFrame === '1d' ? 'MMM DD, YYYY' : 'MMM DD, YYYY HH:00' - const _avgBlockLatency: IData[] = snDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format(timeFormat), value:item?.blockLatency})); + const chartValue = chartDisplay === 'actualProof'? 'actualProofTime' :chartDisplay === 'l1'?'blockL1CreationTime':'blockLatency' + const _avgBlockLatency: IData[] = snDetailData.map((item: any) => ({time:dayjs(item?.timestamp*1000).format(timeFormat), value:item?.[chartValue]})); setAvgBlockLatency_SN(_avgBlockLatency); } - }, [snDetailData]); + }, [snDetailData, chartDisplay]); useEffect(() => { if(avgBlockLatency_SN.length){ @@ -71,6 +78,8 @@ export default function LatencyPanel(props: { }); const sortedDistribution = distribution_data.sort((a:any,b:any)=> a.value-b.value) setDistributionData(sortedDistribution) + + setChartLoading(false); } }, [avgBlockLatency_SN]); @@ -84,11 +93,17 @@ export default function LatencyPanel(props: { }, [ethDetailData]); useEffect(() => { - if(avgBlockLatency_ETH){ + if (avgBlockLatency_ETH.length && avgBlockLatency_SN.length) { setChartLoading(true); + } + }, [network,chartDisplay]); + + useEffect(() => { + if(avgBlockLatency_ETH){ let _avgBlockLatency: IChartData[] = [] avgBlockLatency_ETH.forEach((v,i) => { - let snValue = avgBlockLatency_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') > -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) + let snValue = avgBlockLatency_SN.find((val) => val.time === v.time) + // let snValue = avgBlockLatency_SN.find((val) => dayjs(val.time).diff(dayjs(v.time),'hour') > -2 && dayjs(val.time).diff(dayjs(v.time),'hour') <=2) const _entry: IChartData = {time: timeFrame === '1d' ? dayjs(v.time).format('MMM DD YYYY') : v.time, eth_value:v.value} if (snValue){ _entry.sn_value = snValue?.value; @@ -96,9 +111,6 @@ export default function LatencyPanel(props: { _avgBlockLatency.push(_entry) }); setChartData(_avgBlockLatency.reverse()); - - - setChartLoading(false); } }, [ avgBlockLatency_ETH, avgBlockLatency_SN ]); @@ -109,61 +121,97 @@ export default function LatencyPanel(props: { return (

Block Creation Time

-

at scale the STARK prover and verifier are fastest in class

+ {/*

at scale the STARK prover and verifier are fastest in class

*/} +

-
-
- BlOCK LATENCY (SECONDS) -

- {!snBlockLoading ? - <> {snBlock?.blockLatency.toFixed(2)} - : -
- -
- } -

-
- -
-
- - - - - - - - - - {snBlockData?.detail && snBlockData?.detail.map((val:any, key:number) => { - return ( - - - - - ) - })} - -
BLOCKLATENCY (sec.)
#{val?.block_number}{val?.blockLatency}
+
setChartDisplay('actualProof')} className={`bg-box text-center rounded-lg p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "actualProof" ? "border-4 border-sky-900" : ""}`}> + PROOF GENERATION TIME (SECONDS) +

+ {!snBlockLoading ? + <> {snProof?.actualProofTime.toFixed(2)} + : +
+ +
+ } +

+
+
setChartDisplay('l1')} className={`bg-box text-center rounded-lg p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "l1" ? "border-4 border-sky-900" : ""}`}> + L1 BLOCK CREATION TIME (SECONDS) +

+ {!snBlockLoading ? + <> {snProof?.blockL1CreationTime.toFixed(2)} + : +
+ +
+ } +

+
+
setChartDisplay('l2')} className={`bg-box text-center rounded-lg p-5 cursor-pointer hover:bg-box-hover active:bg-box-active ${chartDisplay === "l2" ? "border-4 border-sky-900" : ""}`}> + L2 BLOCK CREATION TIME (SECONDS) +

+ {!snBlockLoading ? + <> {snBlock?.blockLatency.toFixed(2)} + : +
+ +
+ } +

+
+
+
+
+ + + + + {chartDisplay === "actualProof" && } + {chartDisplay === "l1" && } + {chartDisplay === "l2" && } + + + + + {chartDisplay === "l2" && snBlockData?.detail && snBlockData?.detail.map((val:any, key:number) => { + return ( + + + + + ) + })} + {chartDisplay != "l2" && snProofData?.detail && snProofData?.detail.map((val:any, key:number) => { + return ( + + + {chartDisplay === "actualProof" && } + {chartDisplay === "l1" && } + + ) + })} + +
BLOCKPROOF TIME (sec.)L1 BLOCK LATENCY (sec.)L2 BLOCK LATENCY (sec.)
#{val?.block_number}{val?.blockLatency}
#{val?.block_number}{val?.actualProofTime}{val?.blockL1CreationTime}
-

AVERAGE BLOCK LATENCY

- + {chartDisplay === "actualProof" &&

AVERAGE PROOF GENERATION TIME

} + {chartDisplay != "actualProof" &&

AVERAGE BLOCK LATENCY

} +

DISTRIBUTION

- - VARIANCE: {Numeral(variance).format('0.[0000]a')} + + VARIANCE: {Numeral(variance).format('0.[0000]a')}
LATEST BLOCK: {snBlock?.block_number}
-
UPDATED: {lastUpdated} SECONDS AGO
-
UPDATED: {lastUpdated}s AGO
+
UPDATED: {lastUpdated} SECONDS AGO
+
UPDATED: {lastUpdated}s AGO
TIME FRAME
diff --git a/src/components/navbar/Navbar.tsx b/src/components/navbar/Navbar.tsx index 00a0282..9d45530 100644 --- a/src/components/navbar/Navbar.tsx +++ b/src/components/navbar/Navbar.tsx @@ -5,11 +5,13 @@ import { FaEthereum } from "react-icons/fa"; import { NavLink } from "react-router-dom"; import { SpinnerCircular } from "spinners-react"; import { PriceContext } from '../../context/PriceContext'; +import { AppContext } from '../../context/AppContext'; import { useEthPriceQuery } from '../../queries/price'; export default function Navbar() { const { data:price, isLoading:isLoading } = useEthPriceQuery() const { setEthPriceChange, setEthPrice } = useContext(PriceContext) + const { network, setNetwork } = useContext(AppContext) const priceChange = price?.price_change ? price?.price_change > 0 ? `(+${price?.price_change.toFixed(1)}%)` : `(${price?.price_change.toFixed(1)}%)` @@ -24,27 +26,64 @@ export default function Navbar() { }, [price]) + const handleNetworkToogle = () => { + if (network === "mainnet") { + setNetwork("goerli") + } else { + setNetwork("mainnet") + } + } + return ( -