From d69e65db9486e325cbfe981fe2cf4127c5547c82 Mon Sep 17 00:00:00 2001 From: Louis Tao Date: Wed, 19 Jul 2023 10:59:15 -0400 Subject: [PATCH 01/59] change port --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 9bcb865..614f87a 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -NEXT_PUBLIC_API_URL = http://localhost:8000 +NEXT_PUBLIC_API_URL = http://localhost:8001 NEXT_PUBLIC_GOOGLE_ANALYTICS_CONFIG = G-8GN39Z428L From 7511e5f50501518e1348bd9d68b769090c29cc60 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Wed, 19 Jul 2023 17:33:10 -0400 Subject: [PATCH 02/59] wip --- components/common/chartWrapper/index.tsx | 58 +++++++++++++++-- components/home/charts/funding-rate.tsx | 80 +++++++++++++----------- package-lock.json | 8 +-- 3 files changed, 102 insertions(+), 44 deletions(-) diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 0c042a7..0d99f8d 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -1,5 +1,5 @@ -import { RiLoader5Fill } from 'react-icons/ri'; -import { Box, Button, ButtonGroup, Text, Spinner } from '@chakra-ui/react'; +import { ChevronDownIcon } from '@chakra-ui/icons'; +import { Box, Button, ButtonGroup, Text, Spinner, MenuButton, Menu, MenuList, MenuItemOption, MenuOptionGroup } from '@chakra-ui/react'; interface Toggle { text: string; @@ -7,16 +7,21 @@ interface Toggle { active: boolean; } +export interface CoinSelector { + name: string; + event: () => void; + isChecked: boolean; +} + const Loader = () => function ChartWrapper(props: any) { const { title, loading, - csvFields, - data, controls, zIndex, + coinSelectors, } = props; return ( @@ -55,6 +60,51 @@ function ChartWrapper(props: any) { })} + + { coinSelectors && + + }> + Select coins + + + coinSelector.isChecked).map((coinSelector: CoinSelector) => coinSelector.name)} + > + {coinSelectors.map((coinSelector: CoinSelector, index: number) => { + return ( + coinSelector.event()} + isChecked={coinSelector.isChecked} + > + {coinSelector.name} + + ) + })} + + + +} + {loading && } diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 1619aa0..22381e1 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -8,24 +8,17 @@ import { LineChart, Line } from 'recharts'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" +import { useMediaQuery } from "@chakra-ui/react" import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, - GREEN, - RED, } from "../../../constants"; import { - yaxisFormatterNumber, tooltipFormatter, - tooltipLabelFormatter, xAxisFormatter, formatterPercent, - yaxisFormatter, } from '../../../helpers' import { getTokenHex } from "../../../constants/tokens"; import { @@ -39,9 +32,10 @@ const REQUESTS = [ export default function FundingRate() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [coinKeys, setCoinKeys] = useState([]) - const [formattedData, setFormattedData] = useState([]) + const [coinKeys, setCoinKeys] = useState([]) + const [formattedData, setFormattedData] = useState([]) const [dataFundingRate, loadingFundingRate, errorFundingRate] = useRequest(REQUESTS[0], [], 'chart_data'); + const [coinsSelected, setCoinsSelected] = useState(["ETH", "BTC", "ARB"]); const loading = loadingFundingRate; const error = errorFundingRate; @@ -83,22 +77,7 @@ export default function FundingRate() { map.set(key, existingEntry); }); - // Get the top 10 coins by total funding over the whole time period - const topCoins = Array.from(coinFundingTotals.entries()) - .sort((a, b) => b[1] - a[1]) - .slice(0, 10) - .map(([coin]) => coin); - - // Filter out the coins not in the top 10 for each time period - const result = Array.from(map.values()).map((record: any) => { - Object.keys(record).forEach((coin) => { - if (coin !== 'time' && !topCoins.includes(coin) && coin !== 'unit') { - delete record[coin]; - } - }); - return record; - }); - + const result = Array.from(map.values()); return result; }; @@ -123,12 +102,26 @@ export default function FundingRate() { return coinsArray; }; + const filterBySelectedCoins = (groupedData: GroupedFundingData[]): GroupedFundingData[] => { + const result: GroupedFundingData[] = JSON.parse(JSON.stringify(groupedData)) ; + result.filter(record => { + Object.keys(record).forEach((coin) => { + if (coin !== 'time' && !coinsSelected.includes(coin) && coin !== 'unit') { + delete record[coin]; + } + }); + return record; + }); + return result; + }; + const formatData = () => { if (dataFundingRate) { const groupedData = groupByTime(dataFundingRate); - const uniquieCoins = extractUniqueCoins(groupedData); - setFormattedData(groupedData); - setCoinKeys(uniquieCoins); + const filteredData = filterBySelectedCoins(groupedData); + const uniqueCoins = extractUniqueCoins(groupedData); + setFormattedData(filteredData); + setCoinKeys(uniqueCoins); } } @@ -136,13 +129,31 @@ export default function FundingRate() { if (!loading && !error) { formatData(); } - }, [loading]) + }, [loading, coinsSelected]) + + const coinSelectors = coinKeys.map((coinKey) => { + return ({ + name: coinKey, + event: () => setCoinsSelected(coinsSelected => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { return e !== coinKey }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(); + return newCoinsSelected; + }), + isChecked: coinsSelected.includes(coinKey), + }); + }).sort((a: CoinSelector) => a.isChecked ? -1 : 1); return ( @@ -175,7 +186,7 @@ export default function FundingRate() { /> { - coinKeys.map(((coinName, i) => { + coinsSelected.map(((coinName, i) => { return ( - - Top 10 Coins over time - ) -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 7e7ff36..692fb56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "hyperliquid-web", - "version": "0.1.0", + "name": "hyperliquid-stats-web", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "hyperliquid-web", - "version": "0.1.0", + "name": "hyperliquid-stats-web", + "version": "1.0.0", "dependencies": { "@chakra-ui/icons": "^2.0.19", "@chakra-ui/next-js": "^2.1.3", From 6238fb5370835f77d1ee93273ec7c035930b7f8b Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Wed, 19 Jul 2023 18:13:35 -0400 Subject: [PATCH 03/59] clean up --- components/home/charts/funding-rate.tsx | 57 ++++++++++--------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 22381e1..bf01f7c 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -52,11 +52,14 @@ export default function FundingRate() { }; - const groupByTime = (data: FundingData[]): GroupedFundingData[] => { + const groupByTimeAndFilterUnSelected = (data: FundingData[]): GroupedFundingData[] => { const map = new Map(); const coinFundingTotals = new Map(); data.forEach((item) => { + if (!coinsSelected.includes(item.coin)) { + return; + } const key = item.time; if (!map.has(key)) { map.set(key, { @@ -82,46 +85,30 @@ export default function FundingRate() { }; - const extractUniqueCoins = (formattedData: GroupedFundingData[]): string[] => { + const extractUniqueCoins = (fundingData: FundingData[]): string[] => { const coinSet = new Set(); - for (const data of formattedData) { - Object.keys(data).forEach(coin => { - if (coin !== 'time' && - coin !== 'unit' - ) { - coinSet.add(coin); - } - }); - } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } - return coinsArray; + for (const data of fundingData) { + const {coin} = data; + if (coin !== 'time' && + coin !== 'unit' + ) { + coinSet.add(coin); + } + }; + return Array.from(coinSet); }; - const filterBySelectedCoins = (groupedData: GroupedFundingData[]): GroupedFundingData[] => { - const result: GroupedFundingData[] = JSON.parse(JSON.stringify(groupedData)) ; - result.filter(record => { - Object.keys(record).forEach((coin) => { - if (coin !== 'time' && !coinsSelected.includes(coin) && coin !== 'unit') { - delete record[coin]; - } - }); - return record; - }); - return result; - }; + useEffect(() => { + if (!loading && !error) { + const uniqueCoins = extractUniqueCoins(dataFundingRate); + setCoinKeys(uniqueCoins); + } + }, [loading, dataFundingRate]) const formatData = () => { if (dataFundingRate) { - const groupedData = groupByTime(dataFundingRate); - const filteredData = filterBySelectedCoins(groupedData); - const uniqueCoins = extractUniqueCoins(groupedData); - setFormattedData(filteredData); - setCoinKeys(uniqueCoins); + const groupedAndFilteredData = groupByTimeAndFilterUnSelected(dataFundingRate); + setFormattedData(groupedAndFilteredData); } } From 7edee186c1ee5e9d42091f1f60bb69f00b82b50c Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Thu, 20 Jul 2023 14:42:31 -0400 Subject: [PATCH 04/59] also sort coins alphabetically and minor fixes --- .env | 2 +- components/home/charts/funding-rate.tsx | 11 +++++++++-- components/home/charts/open-interest.tsx | 4 ++-- .../{uniquie-users-coin.tsx => unique-users-coin.tsx} | 0 components/home/main/index.tsx | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) rename components/home/charts/{uniquie-users-coin.tsx => unique-users-coin.tsx} (100%) diff --git a/.env b/.env index 614f87a..6f5ec49 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -NEXT_PUBLIC_API_URL = http://localhost:8001 +NEXT_PUBLIC_API_URL = https://stats-api.hyperliquid.xyz NEXT_PUBLIC_GOOGLE_ANALYTICS_CONFIG = G-8GN39Z428L diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index bf01f7c..f0e6863 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -49,7 +49,6 @@ export default function FundingRate() { type GroupedFundingData = { time: Date; [coin: string]: number | Date; - }; const groupByTimeAndFilterUnSelected = (data: FundingData[]): GroupedFundingData[] => { @@ -118,6 +117,14 @@ export default function FundingRate() { } }, [loading, coinsSelected]) + + const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { + if (a.isChecked !== b.isChecked) { + return a.isChecked ? -1 : 1; + } + return a.name.localeCompare(b.name); + } + const coinSelectors = coinKeys.map((coinKey) => { return ({ name: coinKey, @@ -133,7 +140,7 @@ export default function FundingRate() { }), isChecked: coinsSelected.includes(coinKey), }); - }).sort((a: CoinSelector) => a.isChecked ? -1 : 1); + }).sort((a:CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); return ( { const groupedData = groupByTime(dataOpenInterest); - const uniquieCoins = extractUniqueCoins(groupedData); + const uniqueCoins = extractUniqueCoins(groupedData); setFormattedData(groupedData); - setCoinKeys(uniquieCoins); + setCoinKeys(uniqueCoins); } useEffect(() => { diff --git a/components/home/charts/uniquie-users-coin.tsx b/components/home/charts/unique-users-coin.tsx similarity index 100% rename from components/home/charts/uniquie-users-coin.tsx rename to components/home/charts/unique-users-coin.tsx diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index dc35f6f..a415371 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -12,7 +12,7 @@ import HLPProfitLossChart from '../charts/hlp-liquidator-profit'; import { DateRangeSelect } from '../charts/date-range'; import FundingRateChart from '../charts/funding-rate'; import CumulativeUsersChart from '../charts/cumulative-users'; -import CoinTradesByUsers from "../charts/uniquie-users-coin"; +import CoinTradesByUsers from "../charts/unique-users-coin"; import CumulativeInflowChart from '../charts/cumulative-inflow'; import CumulativeNotionalLiquidatedChart from '../charts/cumulative-notional-liquidated'; import TableLargestUsers from "../tables/largest-users" From b67f40de3020c9a75df6f47e663a443e8ac83840 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Thu, 20 Jul 2023 15:52:29 -0400 Subject: [PATCH 05/59] run prettify --- .vscode/settings.json | 10 +- app/layout.tsx | 29 +- app/providers.tsx | 47 +- components/common/chartWrapper/index.tsx | 226 +++--- components/common/footer/index.tsx | 61 +- components/common/header/index.tsx | 109 ++- components/home/charts/cumulative-inflow.tsx | 317 ++++---- .../charts/cumulative-notional-liquidated.tsx | 574 ++++++++------- components/home/charts/cumulative-users.tsx | 250 +++---- components/home/charts/date-range.tsx | 289 ++++---- components/home/charts/funding-rate.tsx | 352 +++++---- .../home/charts/hlp-liquidator-profit.tsx | 383 +++++----- components/home/charts/liquidity.tsx | 617 ++++++++-------- components/home/charts/open-interest.tsx | 352 +++++---- components/home/charts/top-stats.tsx | 201 +++--- components/home/charts/trader-profit.tsx | 303 ++++---- components/home/charts/unique-users-coin.tsx | 469 ++++++------ components/home/charts/volume-non-hlp.tsx | 633 ++++++++-------- components/home/charts/volume-num-trades.tsx | 683 ++++++++++-------- components/home/main/index.tsx | 170 +++-- components/home/main/styles.ts | 4 +- components/home/tables/largest-users.tsx | 246 ++++--- .../home/tables/liquidated-notional-user.tsx | 246 ++++--- components/home/tables/user-deposits.tsx | 245 ++++--- components/home/tables/user-trade-count.tsx | 248 ++++--- constants/api.ts | 99 +-- constants/index.ts | 10 +- constants/tokens.ts | 68 +- contexts/data.tsx | 59 +- helpers/index.ts | 171 +++-- hooks/useRequest.ts | 48 +- next.config.js | 23 +- package-lock.json | 20 + package.json | 54 +- pre_push_and_pre_commit.sh | 1 + react-table-config.d.ts | 238 +++--- styles/components/alert.ts | 5 +- styles/components/button.ts | 2 +- styles/components/input.ts | 2 +- styles/components/modal.ts | 4 +- styles/components/tabs.ts | 29 +- styles/custom.ts | 284 ++++---- styles/fonts.ts | 84 ++- styles/global.ts | 1 - styles/theme.ts | 5 +- tsconfig.json | 23 +- utils/formatting.ts | 5 +- 47 files changed, 4455 insertions(+), 3844 deletions(-) create mode 100755 pre_push_and_pre_commit.sh diff --git a/.vscode/settings.json b/.vscode/settings.json index b7f2744..3492f91 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { - "typescript.enablePromptUseWorkspaceTsdk": true, - "editor.wordWrap": "wordWrapColumn", - "editor.wordWrapColumn": 140, - "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file + "typescript.enablePromptUseWorkspaceTsdk": true, + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 140, + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/app/layout.tsx b/app/layout.tsx index f7b3ea7..e8a7c74 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,31 +1,24 @@ import { ChakraProvider } from '@chakra-ui/react'; -import theme from "../styles/theme"; -import { Providers } from "./providers"; +import theme from '../styles/theme'; +import { Providers } from './providers'; export const metadata = { title: 'Hyperliquid Stats', description: 'Explore the Hyperliquid protocol’s statistics', -} - -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { +}; +export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + - - - - + + + + - - {children} - + {children} - ) + ); } diff --git a/app/providers.tsx b/app/providers.tsx index 4f6c9b4..4d765e1 100644 --- a/app/providers.tsx +++ b/app/providers.tsx @@ -1,33 +1,28 @@ -'use client' +'use client'; import { useEffect } from 'react'; -import { CacheProvider } from '@chakra-ui/next-js' -import { ChakraProvider } from '@chakra-ui/react' -import TagManager from 'react-gtm-module' +import { CacheProvider } from '@chakra-ui/next-js'; +import { ChakraProvider } from '@chakra-ui/react'; +import TagManager from 'react-gtm-module'; import { Global } from '@emotion/react'; -import theme from "../styles/theme"; +import theme from '../styles/theme'; import { GlobalStyles } from '@/styles/global'; -import { DataContextProvider } from "../contexts/data"; +import { DataContextProvider } from '../contexts/data'; const tagManagerArgs = { gtmId: process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_CONFIG as string }; -export function Providers({ - children -}: { - children: React.ReactNode -}) { +export function Providers({ children }: { children: React.ReactNode }) { + useEffect(() => { + TagManager.initialize(tagManagerArgs); + }, []); - useEffect(() => { - TagManager.initialize(tagManagerArgs) - }, []); - - return ( - - - - - {children} - - - - ) -} \ No newline at end of file + return ( + + + + + {children} + + + + ); +} diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 0d99f8d..c8cfd70 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -1,117 +1,143 @@ import { ChevronDownIcon } from '@chakra-ui/icons'; -import { Box, Button, ButtonGroup, Text, Spinner, MenuButton, Menu, MenuList, MenuItemOption, MenuOptionGroup } from '@chakra-ui/react'; +import { + Box, + Button, + ButtonGroup, + Text, + Spinner, + MenuButton, + Menu, + MenuList, + MenuItemOption, + MenuOptionGroup, +} from '@chakra-ui/react'; interface Toggle { - text: string; - event: () => void; - active: boolean; + text: string; + event: () => void; + active: boolean; } export interface CoinSelector { - name: string; - event: () => void; - isChecked: boolean; + name: string; + event: () => void; + isChecked: boolean; } -const Loader = () => +const Loader = () => ( + + + +); function ChartWrapper(props: any) { - const { - title, - loading, - controls, - zIndex, - coinSelectors, - } = props; + const { title, loading, controls, zIndex, coinSelectors } = props; - return ( - - + + + + + {title} + + - - + {controls && + controls.toggles && + controls.toggles.length > 0 && + controls.toggles.map((toggle: Toggle, index: number) => { + return ( + + ); + })} + + + + {coinSelectors && ( + + }> + Select coins + + + coinSelector.isChecked) + .map((coinSelector: CoinSelector) => coinSelector.name)} > - - {title} - - - - {controls && controls.toggles && controls.toggles.length > 0 && controls.toggles.map((toggle: Toggle, index: number) => { - return ( - - ) - })} - - - - { coinSelectors && - - }> - Select coins - - - coinSelector.isChecked).map((coinSelector: CoinSelector) => coinSelector.name)} - > - {coinSelectors.map((coinSelector: CoinSelector, index: number) => { - return ( - coinSelector.event()} - isChecked={coinSelector.isChecked} - > - {coinSelector.name} - - ) - })} - - - -} - - - - {loading && } - {props.children} + {coinSelectors.map((coinSelector: CoinSelector, index: number) => { + return ( + coinSelector.event()} + isChecked={coinSelector.isChecked} + > + {coinSelector.name} + + ); + })} + + + + )} + - ); + {loading && } + {props.children} + + + ); } -export default ChartWrapper; \ No newline at end of file +export default ChartWrapper; diff --git a/components/common/footer/index.tsx b/components/common/footer/index.tsx index d87fcb2..b625c1c 100644 --- a/components/common/footer/index.tsx +++ b/components/common/footer/index.tsx @@ -1,32 +1,69 @@ -'use client' +'use client'; import React from 'react'; import { Box, Text, Flex, Image, Container } from '@chakra-ui/react'; import { FaGithub, FaTelegram } from 'react-icons/fa'; const Footer = () => { return ( - - - - - - + + + + + + Built By - - Thunderhead + + Thunderhead - + - - + + diff --git a/components/common/header/index.tsx b/components/common/header/index.tsx index 1247588..7a27ecc 100644 --- a/components/common/header/index.tsx +++ b/components/common/header/index.tsx @@ -1,6 +1,6 @@ -'use client' +'use client'; import React from 'react'; -import NextImg from "next/image" +import NextImg from 'next/image'; import { Container, Box, Text, Image, Flex, useMediaQuery } from '@chakra-ui/react'; import * as S from './styles'; @@ -8,15 +8,15 @@ const Header = () => { const [isMobile] = useMediaQuery('(max-width: 700px)'); return ( - + { paddingY='0' > - + - + {!isMobile && ( - - + + Built By - - Thunderhead + + Thunderhead )} {isMobile && ( - - - - + + + + Built By - - Thunderhead + + Thunderhead - )} - ); }; diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index a2c0e73..32ba83a 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -1,164 +1,173 @@ import { - Bar, - Label, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ComposedChart, - Line, - Cell, + Bar, + Label, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, + Cell, } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; import { - CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, - GREEN, - RED, -} from "../../../constants"; -import { - yaxisFormatter, - xAxisFormatter, - formatNumberWithOptions, - tooltipFormatterCurrency, -} from '../../../helpers' -import { - daily_inflow, - cumulative_inflow, -} from "../../../constants/api" + yaxisFormatter, + xAxisFormatter, + formatNumberWithOptions, + tooltipFormatterCurrency, +} from '../../../helpers'; +import { daily_inflow, cumulative_inflow } from '../../../constants/api'; -const REQUESTS = [ - daily_inflow, - cumulative_inflow, -]; +const REQUESTS = [daily_inflow, cumulative_inflow]; export default function CumulativeInflow() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const [formattedData, setFormattedData] = useState([]) - const [dataDailyInflow, loadingDailyInflow, errorDailyInflow] = useRequest(REQUESTS[0], [], 'chart_data'); - const [dataCumulativeInflow, loadingCumulativeInflow, errorCumulativeInflow] = useRequest(REQUESTS[1], [], 'chart_data'); - - const loading = loadingDailyInflow || loadingCumulativeInflow; - const error = errorCumulativeInflow || errorCumulativeInflow; - - interface DailyInflowData { - time: string; - inflow: number; - } - - interface CumulativeInflowData { - time: string; - cumulative_inflow: number; - } - - interface MergedData { - time: Date; - inflow?: number; - cumulative_inflow?: number; - unit: string; + const [isMobile] = useMediaQuery('(max-width: 700px)'); + + const [formattedData, setFormattedData] = useState([]); + const [dataDailyInflow, loadingDailyInflow, errorDailyInflow] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + const [dataCumulativeInflow, loadingCumulativeInflow, errorCumulativeInflow] = useRequest( + REQUESTS[1], + [], + 'chart_data' + ); + + const loading = loadingDailyInflow || loadingCumulativeInflow; + const error = errorCumulativeInflow || errorCumulativeInflow; + + interface DailyInflowData { + time: string; + inflow: number; + } + + interface CumulativeInflowData { + time: string; + cumulative_inflow: number; + } + + interface MergedData { + time: Date; + inflow?: number; + cumulative_inflow?: number; + unit: string; + } + + const mergeInflows = ( + dailyInflows: DailyInflowData[], + cumulativeInflows: CumulativeInflowData[] + ): MergedData[] => { + const map = new Map(); + + dailyInflows.forEach((item) => { + map.set(item.time, { + ...map.get(item.time), + time: new Date(item.time), + inflow: item.inflow, + unit: 'single', + }); + }); + + cumulativeInflows.forEach((item) => { + map.set(item.time, { + ...map.get(item.time), + time: new Date(item.time), + cumulative_inflow: item.cumulative_inflow, + unit: 'single', + }); + }); + + return Array.from(map.values()); + }; + + const formatData = () => { + const formattedData = mergeInflows(dataDailyInflow, dataCumulativeInflow); + setFormattedData(formattedData); + }; + + useEffect(() => { + if (!loading && !errorDailyInflow) { + formatData(); } - - const mergeInflows = (dailyInflows: DailyInflowData[], cumulativeInflows: CumulativeInflowData[]): MergedData[] => { - const map = new Map(); - - dailyInflows.forEach(item => { - map.set(item.time, { ...map.get(item.time), time: new Date(item.time), inflow: item.inflow, unit: "single" }); - }); - - cumulativeInflows.forEach(item => { - map.set(item.time, { ...map.get(item.time), time: new Date(item.time), cumulative_inflow: item.cumulative_inflow, unit: "single" }); - }); - - return Array.from(map.values()); - }; - - - const formatData = () => { - const formattedData = mergeInflows(dataDailyInflow, dataCumulativeInflow); - setFormattedData(formattedData); - }; - - useEffect(() => { - if (!loading && !errorDailyInflow) { - formatData(); - } - }, [loading]) - - return ( - - - - - - - - ''} - contentStyle={{ - textAlign: 'left', - background: "#0A1F1B", - borderColor: "#061412", - color: "#fff", - boxShadow: "0px 0px 7px rgb(0 0 0 / 20%)", - borderRadius: "26px", - maxHeight: "500px" - }} - itemSorter={(item) => { - return Number(item.value) * -1; - }} - - /> - - - {(formattedData || []).map((item: any, i: number) => { - return 0 ? GREEN : RED} /> - })} - - - - - - ) -} \ No newline at end of file + }, [loading]); + + return ( + + + + + + + + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + color: '#fff', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + itemSorter={(item) => { + return Number(item.value) * -1; + }} + /> + + + {(formattedData || []).map((item: any, i: number) => { + return 0 ? GREEN : RED} />; + })} + + + + + + ); +} diff --git a/components/home/charts/cumulative-notional-liquidated.tsx b/components/home/charts/cumulative-notional-liquidated.tsx index 19e3701..db312d6 100644 --- a/components/home/charts/cumulative-notional-liquidated.tsx +++ b/components/home/charts/cumulative-notional-liquidated.tsx @@ -1,314 +1,336 @@ import { - Bar, - Label, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ComposedChart, - Line + Bar, + Label, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { - CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, - BRAND_GREEN_2, - BRAND_GREEN_3 -} from "../../../constants"; + CHART_HEIGHT, + YAXIS_WIDTH, + BRIGHT_GREEN, + BRAND_GREEN_2, + BRAND_GREEN_3, +} from '../../../constants'; import { - tooltipLabelFormatter, - yaxisFormatter, - xAxisFormatter, - tooltipFormatterCurrency, -} from '../../../helpers' -import { getTokenHex } from "../../../constants/tokens"; + tooltipLabelFormatter, + yaxisFormatter, + xAxisFormatter, + tooltipFormatterCurrency, +} from '../../../helpers'; +import { getTokenHex } from '../../../constants/tokens'; import { - cumulative_liquidated_notional, - daily_notional_liquidated_total, - daily_notional_liquidated_by_leverage_type, - daily_notional_liquidated_by_coin, -} from "../../../constants/api" + cumulative_liquidated_notional, + daily_notional_liquidated_total, + daily_notional_liquidated_by_leverage_type, + daily_notional_liquidated_by_coin, +} from '../../../constants/api'; const REQUESTS = [ - cumulative_liquidated_notional, - daily_notional_liquidated_total, - daily_notional_liquidated_by_leverage_type, - daily_notional_liquidated_by_coin, + cumulative_liquidated_notional, + daily_notional_liquidated_total, + daily_notional_liquidated_by_leverage_type, + daily_notional_liquidated_by_coin, ]; export default function CumulativeNotionalLiquidated() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<"COINS" | "MARGIN">('COINS'); - const [formattedDataCoins, setFormattedDataCoins] = useState([]) - const [formattedDataMargin, setFormattedDataMargin] = useState([]) - const [formattedCumulativeVolumeData, setFormattedCumulativeVolumeData] = useState([]); + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); + const [formattedDataCoins, setFormattedDataCoins] = useState([]); + const [formattedDataMargin, setFormattedDataMargin] = useState([]); + const [formattedCumulativeVolumeData, setFormattedCumulativeVolumeData] = useState([]); - const [coinKeys, setCoinKeys] = useState([]) + const [coinKeys, setCoinKeys] = useState([]); - const [dataCumulativeLiquidated, loadingCumulativeLiquidated, errorCumulativeLiquidated] = useRequest(REQUESTS[0], [], 'chart_data'); - const [dataDailyLiquidatedTotal, loadingDailyLiquidatedTotal, errorDailyUsdVolumeTotal] = useRequest(REQUESTS[1], [], 'chart_data'); - const [dataDailyLiquidatedByMargin, loadingDailyLiquidatedByMargin, errorDailyLiquidatedByMargin] = useRequest(REQUESTS[2], [], 'chart_data'); - const [dataDailyLiquidatedByCoins, loadingDailyLiquidatedByCoins, errorDailyLiquidatedByCoins] = useRequest(REQUESTS[3], [], 'chart_data'); + const [dataCumulativeLiquidated, loadingCumulativeLiquidated, errorCumulativeLiquidated] = + useRequest(REQUESTS[0], [], 'chart_data'); + const [dataDailyLiquidatedTotal, loadingDailyLiquidatedTotal, errorDailyUsdVolumeTotal] = + useRequest(REQUESTS[1], [], 'chart_data'); + const [ + dataDailyLiquidatedByMargin, + loadingDailyLiquidatedByMargin, + errorDailyLiquidatedByMargin, + ] = useRequest(REQUESTS[2], [], 'chart_data'); + const [dataDailyLiquidatedByCoins, loadingDailyLiquidatedByCoins, errorDailyLiquidatedByCoins] = + useRequest(REQUESTS[3], [], 'chart_data'); - console.log('test coinKeys', coinKeys); + console.log('test coinKeys', coinKeys); + const loading = + loadingCumulativeLiquidated || + loadingDailyLiquidatedTotal || + loadingDailyLiquidatedByMargin || + loadingDailyLiquidatedByCoins; + const error = + errorCumulativeLiquidated || + errorDailyUsdVolumeTotal || + errorDailyLiquidatedByMargin || + errorDailyLiquidatedByCoins; + type CumulativeLiquidationData = { cumulative: number; time: string }; - const loading = loadingCumulativeLiquidated || loadingDailyLiquidatedTotal || loadingDailyLiquidatedByMargin || loadingDailyLiquidatedByCoins; - const error = errorCumulativeLiquidated || errorDailyUsdVolumeTotal || errorDailyLiquidatedByMargin || errorDailyLiquidatedByCoins; + const formatCumulativeLiquidatedByTime = ( + dataCumulativeLiquidated: CumulativeLiquidationData[] + ): { [key: string]: number } => { + const result: { [key: string]: number } = {}; + for (const data of dataCumulativeLiquidated) { + result[data.time] = data.cumulative; + } + return result; + }; - type CumulativeLiquidationData = { cumulative: number, time: string }; + type VolumeData = { coin: string; daily_usd_volume: number; time: string }; - const formatCumulativeLiquidatedByTime = (dataCumulativeLiquidated: CumulativeLiquidationData[]): { [key: string]: number } => { - const result: { [key: string]: number } = {}; - for (const data of dataCumulativeLiquidated) { - result[data.time] = data.cumulative; - } - return result; - }; + type LiquidationData = { + time: string; + leverage_type: 'Cross' | 'Isolated'; + daily_notional_liquidated: number; + }; - type VolumeData = { coin: string, daily_usd_volume: number, time: string }; + const formatLiquidatedByMargin = ( + dataDailyLiquidatedByMargin: LiquidationData[], + formattedCumulativeLiquidatedByTime: { [key: string]: number } + ): any[] => { + const temp: { [key: string]: any } = {}; + for (const data of dataDailyLiquidatedByMargin) { + if (!temp[data.time]) { + temp[data.time] = { all: 0 }; + } + temp[data.time][data.leverage_type] = data.daily_notional_liquidated; + temp[data.time].all += data.daily_notional_liquidated; + } + const result: any[] = Object.entries(temp).map((item: any) => { + return { + time: new Date(item[0]), + crossed: item[1].hasOwnProperty('Cross') ? item[1].Cross : 0, + isolated: item[1].hasOwnProperty('Isolated') ? item[1].Isolated : 0, + all: item[1].all, + cumulative: formattedCumulativeLiquidatedByTime[item[0]], + }; + }); + return result; + }; - type LiquidationData = { - time: string; - leverage_type: 'Cross' | 'Isolated'; - daily_notional_liquidated: number; - }; + type FormattedCoinTradesData = any[]; + + const formatDailyTradesByCoins = ( + dataDailyTradesByCoin: { time: string; coin: string; daily_notional_liquidated: number }[], + formattedCumulativeByTime: { [key: string]: number } + ): FormattedCoinTradesData[] => { + const temp: { [key: string]: { all: number; [coin: string]: number } } = {}; + for (const data of dataDailyTradesByCoin) { + if (!temp[data.time]) { + temp[data.time] = { all: 0 }; + } + temp[data.time][data.coin] = data.daily_notional_liquidated; + temp[data.time].all += data.daily_notional_liquidated; + } - const formatLiquidatedByMargin = ( - dataDailyLiquidatedByMargin: LiquidationData[], - formattedCumulativeLiquidatedByTime: { [key: string]: number } - ): any[] => { + const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { + const sortedEntries = Object.entries(obj).sort( + ([, aVolume], [, bVolume]) => bVolume - aVolume + ); + const top10Entries = sortedEntries.slice(0, 10); + const otherEntries = sortedEntries.slice(10); - const temp: { [key: string]: any } = {}; - for (const data of dataDailyLiquidatedByMargin) { - if (!temp[data.time]) { - temp[data.time] = { all: 0 }; - } - temp[data.time][data.leverage_type] = data.daily_notional_liquidated; - temp[data.time].all += data.daily_notional_liquidated; - } - const result: any[] = Object.entries(temp).map((item: any) => { - return { - time: new Date(item[0]), - crossed: item[1].hasOwnProperty('Cross') ? item[1].Cross : 0, - isolated: item[1].hasOwnProperty('Isolated') ? item[1].Isolated : 0, - all: item[1].all, - cumulative: formattedCumulativeLiquidatedByTime[item[0]] - }; - }); - return result; + const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); + return { + ...Object.fromEntries(top10Entries), + Other: otherVolume, + }; }; - type FormattedCoinTradesData = any[]; + const result: any[] = Object.entries(temp).map(([time, volumes]) => { + const top10Volumes = sortAndSliceTop10(volumes); + return { + time: new Date(time), + ...top10Volumes, + cumulative: formattedCumulativeByTime[time as any], + unit: '', + }; + }); + return result; + }; - const formatDailyTradesByCoins = ( - dataDailyTradesByCoin: { time: string, coin: string, daily_notional_liquidated: number }[], - formattedCumulativeByTime: { [key: string]: number }, - ): FormattedCoinTradesData[] => { - const temp: { [key: string]: { all: number, [coin: string]: number } } = {}; - for (const data of dataDailyTradesByCoin) { - if (!temp[data.time]) { - temp[data.time] = { all: 0 }; - } - temp[data.time][data.coin] = data.daily_notional_liquidated; - temp[data.time].all += data.daily_notional_liquidated; + const extractUniqueCoins = (formattedData: any[]): string[] => { + const coinSet = new Set(); + for (const data of formattedData) { + Object.keys(data).forEach((coin) => { + if (coin !== 'time' && coin !== 'unit' && coin !== 'cumulative' && coin !== 'all') { + coinSet.add(coin); } + }); + } + const coinsArray = Array.from(coinSet); + if (coinsArray.includes('Other')) { + const index = coinsArray.indexOf('Other'); + coinsArray.splice(index, 1); + coinsArray.push('Other'); + } + return coinsArray; + }; - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj) - .sort(([, aVolume], [, bVolume]) => bVolume - aVolume); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); - - const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); - return { - ...Object.fromEntries(top10Entries), - Other: otherVolume - }; - }; - - const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); - return { - time: new Date(time), - ...top10Volumes, - cumulative: formattedCumulativeByTime[time as any], - unit: '', - }; - }); - return result; - }; - - const extractUniqueCoins = (formattedData: any[]): string[] => { - const coinSet = new Set(); - for (const data of formattedData) { - Object.keys(data).forEach(coin => { - if (coin !== 'time' && - coin !== 'unit' && coin !== 'cumulative' && coin !== 'all' - ) { - coinSet.add(coin); - } - }); - } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } - return coinsArray; - }; + const formatData = () => { + const formattedCumulativeLiquidatedByTime = + formatCumulativeLiquidatedByTime(dataCumulativeLiquidated); + const formattedVolumeByMargin = formatLiquidatedByMargin( + dataDailyLiquidatedByMargin, + formattedCumulativeLiquidatedByTime + ); + const formattedDailyTradesByCoins = formatDailyTradesByCoins( + dataDailyLiquidatedByCoins, + formattedCumulativeLiquidatedByTime + ); + setCoinKeys(extractUniqueCoins(formattedDailyTradesByCoins)); + setFormattedDataMargin(formattedVolumeByMargin); + setFormattedDataCoins(formattedDailyTradesByCoins); + console.log('dev formattedDailyTradesByCoins', formattedDailyTradesByCoins); + }; - const formatData = () => { - const formattedCumulativeLiquidatedByTime = formatCumulativeLiquidatedByTime(dataCumulativeLiquidated) - const formattedVolumeByMargin = formatLiquidatedByMargin(dataDailyLiquidatedByMargin, formattedCumulativeLiquidatedByTime); - const formattedDailyTradesByCoins = formatDailyTradesByCoins(dataDailyLiquidatedByCoins, formattedCumulativeLiquidatedByTime); - setCoinKeys(extractUniqueCoins(formattedDailyTradesByCoins)); - setFormattedDataMargin(formattedVolumeByMargin); - setFormattedDataCoins(formattedDailyTradesByCoins); - console.log('dev formattedDailyTradesByCoins', formattedDailyTradesByCoins) - }; + const controls = { + toggles: [ + { + text: 'Coins', + event: () => setDataMode('COINS'), + active: dataMode === 'COINS', + }, + { + text: 'Cross / Isolated Margin', + event: () => setDataMode('MARGIN'), + active: dataMode === 'MARGIN', + }, + ], + }; - const controls = { - toggles: [ - { - text: "Coins", - event: () => setDataMode('COINS'), - active: dataMode === 'COINS', - }, - { - text: "Cross / Isolated Margin", - event: () => setDataMode('MARGIN'), - active: dataMode === 'MARGIN', - }, - ] + useEffect(() => { + if (!loading) { + formatData(); } + }, [loading]); - useEffect(() => { - if (!loading) { - formatData(); - } - }, [loading]) - - return ( - + + - - - - - - - { - return Number(item.value) * -1; - }} - /> - - { - dataMode === 'COINS' && ( - <> - { - coinKeys && coinKeys.map(((coinName, i) => { - return ( - - ) - })) - } - - ) - } - { - dataMode === 'MARGIN' && ( - <> - - - - ) - } - + + + + { + return Number(item.value) * -1; + }} + /> + + {dataMode === 'COINS' && ( + <> + {coinKeys && + coinKeys.map((coinName, i) => { + return ( + - - - - - Top 10 Coins grouped daily and remaining coins grouped by Other - - - - ) + ); + })} + + )} + {dataMode === 'MARGIN' && ( + <> + + + + )} + + + + + Top 10 Coins grouped daily and remaining coins grouped by Other + + + ); } diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index 0e766c6..828626b 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -1,141 +1,143 @@ import { - Bar, - Label, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ComposedChart, - Line + Bar, + Label, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useMediaQuery } from "@chakra-ui/react" +import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; +import { xAxisFormatter, yaxisFormatterNumber, tooltipFormatter } from '../../../helpers'; import { - CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, - GREEN, -} from "../../../constants"; -import { - xAxisFormatter, - yaxisFormatterNumber, - tooltipFormatter -} from "../../../helpers" -import { - cumulative_new_users, - daily_unique_users, - daily_unique_users_by_coin, -} from "../../../constants/api" + cumulative_new_users, + daily_unique_users, + daily_unique_users_by_coin, +} from '../../../constants/api'; -const REQUESTS = [ - cumulative_new_users, - daily_unique_users, - daily_unique_users_by_coin, -]; +const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; export default function CumulativeUsers() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [formattedData, setFormattedData] = useState([]) - const [unquieKeys, setUnquieKeys] = useState([]) + const [formattedData, setFormattedData] = useState([]); + const [unquieKeys, setUnquieKeys] = useState([]); - const [dataCumulativeNewUsers, loadingCumulativeNewUsers, errorCumulativeNewUsers] = useRequest(REQUESTS[0], [], 'chart_data'); - const [dataDailyUniqueUsers, loadingDailyUniqueUsers, errorDailyUniqueUsers] = useRequest(REQUESTS[1], [], 'chart_data'); - const [dataDailyUniqueUsersByCoin, loadingDailyUniqueUsersByCoin, errorDailyUniqueUsersByCoin] = useRequest(REQUESTS[2], [], 'chart_data'); + const [dataCumulativeNewUsers, loadingCumulativeNewUsers, errorCumulativeNewUsers] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + const [dataDailyUniqueUsers, loadingDailyUniqueUsers, errorDailyUniqueUsers] = useRequest( + REQUESTS[1], + [], + 'chart_data' + ); + const [dataDailyUniqueUsersByCoin, loadingDailyUniqueUsersByCoin, errorDailyUniqueUsersByCoin] = + useRequest(REQUESTS[2], [], 'chart_data'); - const loading = loadingCumulativeNewUsers || loadingDailyUniqueUsers; - const error = errorCumulativeNewUsers || errorDailyUniqueUsers; + const loading = loadingCumulativeNewUsers || loadingDailyUniqueUsers; + const error = errorCumulativeNewUsers || errorDailyUniqueUsers; - type CumulativeUniqueUsers = { time: string, cumulative_new_users: number, daily_new_users: number }; + type CumulativeUniqueUsers = { + time: string; + cumulative_new_users: number; + daily_new_users: number; + }; - const formatdataCumulativeUniqueUsers = (dataCumulativeUsdVolume: CumulativeUniqueUsers[]): any[] => { - return dataCumulativeUsdVolume.map((item: CumulativeUniqueUsers) => ({ - ...item, - time: new Date(item.time), - })); - }; + const formatdataCumulativeUniqueUsers = ( + dataCumulativeUsdVolume: CumulativeUniqueUsers[] + ): any[] => { + return dataCumulativeUsdVolume.map((item: CumulativeUniqueUsers) => ({ + ...item, + time: new Date(item.time), + })); + }; - const formatData = () => { - const formattedData = formatdataCumulativeUniqueUsers(dataCumulativeNewUsers); - setFormattedData(formattedData); - }; + const formatData = () => { + const formattedData = formatdataCumulativeUniqueUsers(dataCumulativeNewUsers); + setFormattedData(formattedData); + }; - useEffect(() => { - if (!loading) { - formatData(); - } - }, [loading]) + useEffect(() => { + if (!loading) { + formatData(); + } + }, [loading]); - return ( - - - - - - - - ''} - contentStyle={{ - textAlign: 'left', - background: "#0A1F1B", - borderColor: "#061412", - boxShadow: "0px 0px 7px rgb(0 0 0 / 20%)", - borderRadius: "26px", - maxHeight: "500px" - }} - itemSorter={(item) => { - return Number(item.value) * -1; - }} - filterNull={true} - /> - - - - - - - ) -} \ No newline at end of file + return ( + + + + + + + + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + itemSorter={(item) => { + return Number(item.value) * -1; + }} + filterNull={true} + /> + + + + + + + ); +} diff --git a/components/home/charts/date-range.tsx b/components/home/charts/date-range.tsx index cf64013..faa6fc8 100644 --- a/components/home/charts/date-range.tsx +++ b/components/home/charts/date-range.tsx @@ -1,10 +1,10 @@ -import { useState, useEffect, useContext } from 'react' -import Select from 'react-dropdown-select' -import { Box, Text } from "@chakra-ui/react" -import moment from 'moment' +import { useState, useEffect, useContext } from 'react'; +import Select from 'react-dropdown-select'; +import { Box, Text } from '@chakra-ui/react'; +import moment from 'moment'; import { DateRange } from 'react-date-range'; import strftime from 'strftime'; -import { DataContext } from "../../../contexts/data"; +import { DataContext } from '../../../contexts/data'; import 'react-date-range/dist/styles.css'; import 'react-date-range/dist/theme/default.css'; @@ -13,109 +13,118 @@ const DATA_START_DATE = new Date('2023-05-10'); const DATE_NOW = new Date(); export const DateRangeSelect = () => { - const { dates, setDates } = useContext(DataContext); - - const [selectedDateRangeOption, setSelectedDateRangeOption] = useState(null) - const [rangeState, setRangeState] = useState<{ startDate: Date | undefined, endDate: Date | undefined, key: string }[]>([ - { - startDate: DATA_START_DATE, - endDate: DATE_NOW, - key: 'selection' - } + const { dates, setDates } = useContext(DataContext); + + const [selectedDateRangeOption, setSelectedDateRangeOption] = useState(null); + const [rangeState, setRangeState] = useState< + { startDate: Date | undefined; endDate: Date | undefined; key: string }[] + >([ + { + startDate: DATA_START_DATE, + endDate: DATE_NOW, + key: 'selection', + }, + ]); + + console.log({ + startDate: DATA_START_DATE, + endDate: new Date(), + key: 'selection', + }); + + const [dataRange, setDataRange] = useState({ fromValue: DATA_START_DATE, toValue: DATE_NOW }); + + const onChange = (selectedDates: any) => { + const [start, end] = selectedDates; + const from = start ? strftime('%Y-%m-%d', new Date(start)) : undefined; + const to = end ? strftime('%Y-%m-%d', end) : undefined; + if (from === to) return; + setDataRange({ fromValue: start, toValue: end }); + setDates({ from, to }); + }; + + const dateRangeOptions = [ + { + label: 'Last Month', + id: 1, + }, + { + label: 'All Time', + id: 4, + isDefault: true, + }, + ]; + + useEffect(() => { + setRangeState([ + { + startDate: dataRange.fromValue, + endDate: dataRange.toValue, + key: 'selection', + }, ]); + }, [dataRange.fromValue, dataRange.toValue]); - console.log({ - startDate: DATA_START_DATE, - endDate: new Date(), - key: 'selection' - }) - - const [dataRange, setDataRange] = useState( - { fromValue: DATA_START_DATE, toValue: DATE_NOW } - ) - - const onChange = (selectedDates: any) => { - const [start, end] = selectedDates; - const from = start ? strftime('%Y-%m-%d', new Date(start)) : undefined - const to = end ? strftime('%Y-%m-%d', end) : undefined - if (from === to) return; - setDataRange({ fromValue: start, toValue: end }) - setDates({ from, to }); - }; - - const dateRangeOptions = [{ - label: "Last Month", - id: 1 - }, { - label: "All Time", - id: 4, - isDefault: true - }] - - useEffect(() => { - setRangeState([ - { - startDate: dataRange.fromValue, - endDate: dataRange.toValue, - key: 'selection' - } - ]) - }, [dataRange.fromValue, dataRange.toValue]) - - const onSelectItem = (option: { id: number, label: string, isDefault?: boolean }) => { - - if (option.id == ALL_TIME_ID) { - onChange([null, null]) - return; - } - const end = new Date() - const start = moment().subtract(option.id, 'month').toDate(); - setSelectedDateRangeOption(option.id) - if (option.id == ALL_TIME_ID) { - onChange([null, null]) - } else { - onChange([start, end]) - } + const onSelectItem = (option: { id: number; label: string; isDefault?: boolean }) => { + if (option.id == ALL_TIME_ID) { + onChange([null, null]); + return; } - - useEffect(() => { - let selected = false - for (const option of dateRangeOptions) { - if (option.isDefault) { - selected = true - onSelectItem(option) - break - } - } - if (!selected) { - onSelectItem(dateRangeOptions[0]) - } - }, []) - - const onDateRangeChange = (item: any) => { - setRangeState([item.selection]) - if (item.selection.startDate == item.selection.endDate) { - return - } - onChange([item.selection.startDate, item.selection.endDate]) + const end = new Date(); + const start = moment().subtract(option.id, 'month').toDate(); + setSelectedDateRangeOption(option.id); + if (option.id == ALL_TIME_ID) { + onChange([null, null]); + } else { + onChange([start, end]); + } + }; + + useEffect(() => { + let selected = false; + for (const option of dateRangeOptions) { + if (option.isDefault) { + selected = true; + onSelectItem(option); + break; + } + } + if (!selected) { + onSelectItem(dateRangeOptions[0]); } + }, []); - const customContentRenderer = ({ props, state }: any) => { - const start = dataRange.fromValue && dataRange.fromValue.toISOString().slice(0, 10) - const end = dataRange.toValue && (dataRange.toValue as Date).toISOString().slice(0, 10) + const onDateRangeChange = (item: any) => { + setRangeState([item.selection]); + if (item.selection.startDate == item.selection.endDate) { + return; + } + onChange([item.selection.startDate, item.selection.endDate]); + }; - return (
- {dataRange.fromValue && dataRange.toValue && `${strftime('%m-%d-%y', dataRange.fromValue)} to ${strftime('%m-%d-%y', dataRange.toValue)}`} - {(!dataRange.fromValue || !dataRange.toValue) && 'All time'} -
) - }; + const customContentRenderer = ({ props, state }: any) => { + const start = dataRange.fromValue && dataRange.fromValue.toISOString().slice(0, 10); + const end = dataRange.toValue && (dataRange.toValue as Date).toISOString().slice(0, 10); - const customDropdownRenderer = ({ props, state, methods }: any) => { - const regexp = new RegExp(state.search, 'i'); + return ( +
+ {dataRange.fromValue && + dataRange.toValue && + `${strftime('%m-%d-%y', dataRange.fromValue)} to ${strftime( + '%m-%d-%y', + dataRange.toValue + )}`} + {(!dataRange.fromValue || !dataRange.toValue) && 'All time'} +
+ ); + }; + + const customDropdownRenderer = ({ props, state, methods }: any) => { + const regexp = new RegExp(state.search, 'i'); - return ( - - {/* + return ( + + {/* {props.options .filter((item: any) => regexp.test(item[props.searchBy] || item[props.labelField])) .map((option: any, index: any) => { @@ -135,44 +144,44 @@ export const DateRangeSelect = () => { ); })} */} - - - - - ); - }; - - const selectedOption = dateRangeOptions.find(option => option.id === selectedDateRangeOption); - const values = selectedOption ? [selectedOption] : []; - - const handleSelectChange = () => { - // console.log('handleSelectChange') - } - - return ( - - + + ); +}; diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index f0e6863..4dcda07 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -1,200 +1,196 @@ import { - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - LineChart, - Line + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + LineChart, + Line, } from 'recharts'; -import { useMediaQuery } from "@chakra-ui/react" +import { useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; -import { - CHART_HEIGHT, -} from "../../../constants"; -import { - tooltipFormatter, - xAxisFormatter, - formatterPercent, -} from '../../../helpers' -import { getTokenHex } from "../../../constants/tokens"; -import { - funding_rate, -} from "../../../constants/api" +import { CHART_HEIGHT } from '../../../constants'; +import { tooltipFormatter, xAxisFormatter, formatterPercent } from '../../../helpers'; +import { getTokenHex } from '../../../constants/tokens'; +import { funding_rate } from '../../../constants/api'; -const REQUESTS = [ - funding_rate -]; +const REQUESTS = [funding_rate]; export default function FundingRate() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const [coinKeys, setCoinKeys] = useState([]) - const [formattedData, setFormattedData] = useState([]) - const [dataFundingRate, loadingFundingRate, errorFundingRate] = useRequest(REQUESTS[0], [], 'chart_data'); - const [coinsSelected, setCoinsSelected] = useState(["ETH", "BTC", "ARB"]); - - const loading = loadingFundingRate; - const error = errorFundingRate; - - type FundingData = { - coin: string, - sum_funding: number, - time: string, - }; - - type GroupedFundingData = { - time: Date; - [coin: string]: number | Date; - }; - - const groupByTimeAndFilterUnSelected = (data: FundingData[]): GroupedFundingData[] => { - const map = new Map(); - const coinFundingTotals = new Map(); - - data.forEach((item) => { - if (!coinsSelected.includes(item.coin)) { - return; - } - const key = item.time; - if (!map.has(key)) { - map.set(key, { - time: new Date(key), - unit: "%" - }); - } - - const existingEntry = map.get(key); + const [isMobile] = useMediaQuery('(max-width: 700px)'); + + const [coinKeys, setCoinKeys] = useState([]); + const [formattedData, setFormattedData] = useState([]); + const [dataFundingRate, loadingFundingRate, errorFundingRate] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + const [coinsSelected, setCoinsSelected] = useState(['ETH', 'BTC', 'ARB']); + + const loading = loadingFundingRate; + const error = errorFundingRate; + + type FundingData = { + coin: string; + sum_funding: number; + time: string; + }; + + type GroupedFundingData = { + time: Date; + [coin: string]: number | Date; + }; + + const groupByTimeAndFilterUnSelected = (data: FundingData[]): GroupedFundingData[] => { + const map = new Map(); + const coinFundingTotals = new Map(); + + data.forEach((item) => { + if (!coinsSelected.includes(item.coin)) { + return; + } + const key = item.time; + if (!map.has(key)) { + map.set(key, { + time: new Date(key), + unit: '%', + }); + } - const value = (existingEntry[item.coin] || 0) + item.sum_funding; + const existingEntry = map.get(key); - existingEntry[item.coin] = value * 100; + const value = (existingEntry[item.coin] || 0) + item.sum_funding; - // Update total funding for the coin - coinFundingTotals.set(item.coin, value * 100); + existingEntry[item.coin] = value * 100; - map.set(key, existingEntry); - }); + // Update total funding for the coin + coinFundingTotals.set(item.coin, value * 100); - const result = Array.from(map.values()); - return result; - }; + map.set(key, existingEntry); + }); + const result = Array.from(map.values()); + return result; + }; - const extractUniqueCoins = (fundingData: FundingData[]): string[] => { - const coinSet = new Set(); - for (const data of fundingData) { - const {coin} = data; - if (coin !== 'time' && - coin !== 'unit' - ) { - coinSet.add(coin); - } - }; - return Array.from(coinSet); - }; - - useEffect(() => { - if (!loading && !error) { - const uniqueCoins = extractUniqueCoins(dataFundingRate); - setCoinKeys(uniqueCoins); - } - }, [loading, dataFundingRate]) - - const formatData = () => { - if (dataFundingRate) { - const groupedAndFilteredData = groupByTimeAndFilterUnSelected(dataFundingRate); - setFormattedData(groupedAndFilteredData); - } + const extractUniqueCoins = (fundingData: FundingData[]): string[] => { + const coinSet = new Set(); + for (const data of fundingData) { + const { coin } = data; + if (coin !== 'time' && coin !== 'unit') { + coinSet.add(coin); + } } + return Array.from(coinSet); + }; - useEffect(() => { - if (!loading && !error) { - formatData(); - } - }, [loading, coinsSelected]) + useEffect(() => { + if (!loading && !error) { + const uniqueCoins = extractUniqueCoins(dataFundingRate); + setCoinKeys(uniqueCoins); + } + }, [loading, dataFundingRate]); + const formatData = () => { + if (dataFundingRate) { + const groupedAndFilteredData = groupByTimeAndFilterUnSelected(dataFundingRate); + setFormattedData(groupedAndFilteredData); + } + }; - const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { - if (a.isChecked !== b.isChecked) { - return a.isChecked ? -1 : 1; - } - return a.name.localeCompare(b.name); + useEffect(() => { + if (!loading && !error) { + formatData(); } + }, [loading, coinsSelected]); - const coinSelectors = coinKeys.map((coinKey) => { - return ({ - name: coinKey, - event: () => setCoinsSelected(coinsSelected => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { return e !== coinKey }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(); - return newCoinsSelected; - }), - isChecked: coinsSelected.includes(coinKey), - }); - }).sort((a:CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); - - return ( - - - - - - - ''} - contentStyle={{ - textAlign: 'left', - background: "#0A1F1B", - borderColor: "#061412", - color: "#fff", - boxShadow: "0px 0px 7px rgb(0 0 0 / 20%)", - borderRadius: "26px", - maxHeight: "500px" - }} - itemSorter={(item) => { - return Number(item.value) * -1; - }} - /> - - { - coinsSelected.map(((coinName, i) => { - return ( - - ) - })) - } - - - - ) + const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { + if (a.isChecked !== b.isChecked) { + return a.isChecked ? -1 : 1; + } + return a.name.localeCompare(b.name); + }; + + const coinSelectors = coinKeys + .map((coinKey: string) => { + return { + name: coinKey, + event: () => + setCoinsSelected((coinsSelected) => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(); + return newCoinsSelected; + }), + isChecked: coinsSelected.includes(coinKey), + }; + }) + .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); + + return ( + + + + + + + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + color: '#fff', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + itemSorter={(item) => { + return Number(item.value) * -1; + }} + /> + + {coinsSelected.map((coinName, i) => { + return ( + + ); + })} + + + + ); } diff --git a/components/home/charts/hlp-liquidator-profit.tsx b/components/home/charts/hlp-liquidator-profit.tsx index a73605e..95b66ce 100644 --- a/components/home/charts/hlp-liquidator-profit.tsx +++ b/components/home/charts/hlp-liquidator-profit.tsx @@ -1,197 +1,224 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react'; import { - Bar, - Cell, - CartesianGrid, - ComposedChart, - Legend, - Line, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis + Bar, + Cell, + CartesianGrid, + ComposedChart, + Legend, + Line, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, } from 'recharts'; -import { sortBy, maxBy, minBy } from 'lodash' -import { useMediaQuery } from "@chakra-ui/react" +import { sortBy, maxBy, minBy } from 'lodash'; +import { useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import { hlp_liquidator_pnl, cumulative_hlp_liquidator_pnl, hlp_liquidator_pnl_false, cumulative_hlp_liquidator_pnl_false } from "../../../constants/api" -import ChartWrapper from '../../common/chartWrapper'; -import { - CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, - GREEN, - RED, -} from "../../../constants"; import { - yaxisFormatter, - xAxisFormatter, - tooltipFormatterCurrency, -} from '../../../helpers' + hlp_liquidator_pnl, + cumulative_hlp_liquidator_pnl, + hlp_liquidator_pnl_false, + cumulative_hlp_liquidator_pnl_false, +} from '../../../constants/api'; +import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; +import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency } from '../../../helpers'; const REQUESTS = [ - hlp_liquidator_pnl, - cumulative_hlp_liquidator_pnl, - hlp_liquidator_pnl_false, - cumulative_hlp_liquidator_pnl_false, + hlp_liquidator_pnl, + cumulative_hlp_liquidator_pnl, + hlp_liquidator_pnl_false, + cumulative_hlp_liquidator_pnl_false, ]; export default function HLPProfitLossChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const [chartDataLiquidator, setChartDataLiquidator] = useState(null); - const [chartDataNonLiquidator, setChartDataNonLiquidator] = useState(null); - const [dataMode, setDataMode] = useState<"HLP" | "NON_HLP">('HLP'); - const [dataHLPLiquidatorPNL, loadingHLPLiquidatorPNL, errorHLPLiquidatorPNL] = useRequest(REQUESTS[0], [], 'chart_data'); - const [dataCumulativeHLPLiquidatorPNL, loadingCumulativeHLPLiquidatorPNL, errorCumulative_HLPLiquidatorPNL] = useRequest(REQUESTS[1], [], 'chart_data'); - const [dataNonLiquidator, loadingNonLiquidator, errorNonLiquidator] = useRequest(REQUESTS[2], [], 'chart_data'); - const [dataCumulativeNonLiquidatorPNL, loadingCumulativeNonLiquidatorPNL, errorCumulative_NonLiquidatorPNL] = useRequest(REQUESTS[3], [], 'chart_data'); - + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const formatTradingData = (dataHLPLiquidatorPNL: any, dataCumulativeHLPLiquidatorPNL: any) => { - let currentProfitCumulative = 0; - let currentLossCumulative = 0; + const [chartDataLiquidator, setChartDataLiquidator] = useState(null); + const [chartDataNonLiquidator, setChartDataNonLiquidator] = useState(null); + const [dataMode, setDataMode] = useState<'HLP' | 'NON_HLP'>('HLP'); + const [dataHLPLiquidatorPNL, loadingHLPLiquidatorPNL, errorHLPLiquidatorPNL] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + const [ + dataCumulativeHLPLiquidatorPNL, + loadingCumulativeHLPLiquidatorPNL, + errorCumulative_HLPLiquidatorPNL, + ] = useRequest(REQUESTS[1], [], 'chart_data'); + const [dataNonLiquidator, loadingNonLiquidator, errorNonLiquidator] = useRequest( + REQUESTS[2], + [], + 'chart_data' + ); + const [ + dataCumulativeNonLiquidatorPNL, + loadingCumulativeNonLiquidatorPNL, + errorCumulative_NonLiquidatorPNL, + ] = useRequest(REQUESTS[3], [], 'chart_data'); - let data = sortBy(dataHLPLiquidatorPNL, (i) => Date.parse(i.time)).map((dataItem, index) => { - const cumulative_pnl = dataCumulativeHLPLiquidatorPNL[index]?.cumulative_pnl || 0; - return { - cumulative_pnl: cumulative_pnl, - total_pnl: dataItem.total_pnl, - timestamp: new Date(dataItem.time), - profit_cumulative: cumulative_pnl > 0 ? cumulative_pnl : 0, - loss_cumulative: cumulative_pnl < 0 ? cumulative_pnl : 0, - unit: "single", - }; - }); + const formatTradingData = (dataHLPLiquidatorPNL: any, dataCumulativeHLPLiquidatorPNL: any) => { + let currentProfitCumulative = 0; + let currentLossCumulative = 0; - let result; + let data = sortBy(dataHLPLiquidatorPNL, (i) => Date.parse(i.time)).map((dataItem, index) => { + const cumulative_pnl = dataCumulativeHLPLiquidatorPNL[index]?.cumulative_pnl || 0; + return { + cumulative_pnl: cumulative_pnl, + total_pnl: dataItem.total_pnl, + timestamp: new Date(dataItem.time), + profit_cumulative: cumulative_pnl > 0 ? cumulative_pnl : 0, + loss_cumulative: cumulative_pnl < 0 ? cumulative_pnl : 0, + unit: 'single', + }; + }); - if (data && data.length > 0) { - const maxProfit = maxBy(data, item => item.profit_cumulative)?.profit_cumulative || 0; - const maxLoss = minBy(data, item => item.loss_cumulative)?.loss_cumulative || 0; - const maxPnl = maxBy(data, item => item.total_pnl)?.total_pnl || 0; - const minPnl = minBy(data, item => item.total_pnl)?.total_pnl || 0; - const maxCurrentCumulativePnl = maxBy(data, item => item.cumulative_pnl)?.cumulative_pnl || 0; - const minCurrentCumulativePnl = minBy(data, item => item.cumulative_pnl)?.cumulative_pnl || 0; + let result; - const stats = { - maxProfit, - maxLoss, - currentProfitCumulative, - currentLossCumulative, - maxCurrentCumulativeProfitLoss: Math.max(currentProfitCumulative, -currentLossCumulative), - minAbsPnl: minPnl, - maxAbsPnl: Math.max( - Math.abs(maxPnl), - Math.abs(minPnl), - ), - maxAbsCumulativePnl: Math.max( - Math.abs(maxCurrentCumulativePnl), - Math.abs(minCurrentCumulativePnl) - ), - } - result = { - data, - stats, - }; - } - return result; - }; - - - useEffect(() => { - if (dataHLPLiquidatorPNL.length > 0 && dataCumulativeHLPLiquidatorPNL.length > 0 && dataNonLiquidator.length > 0 && dataCumulativeNonLiquidatorPNL.length > 0) { - const formattedTradingData = formatTradingData(dataHLPLiquidatorPNL, dataCumulativeHLPLiquidatorPNL); - const formattedDataNonLiquidator = formatTradingData(dataNonLiquidator, dataCumulativeNonLiquidatorPNL); - setChartDataLiquidator(formattedTradingData); - setChartDataNonLiquidator(formattedDataNonLiquidator); - } - }, [dataHLPLiquidatorPNL, dataCumulativeHLPLiquidatorPNL, dataNonLiquidator, dataCumulativeNonLiquidatorPNL]); + if (data && data.length > 0) { + const maxProfit = maxBy(data, (item) => item.profit_cumulative)?.profit_cumulative || 0; + const maxLoss = minBy(data, (item) => item.loss_cumulative)?.loss_cumulative || 0; + const maxPnl = maxBy(data, (item) => item.total_pnl)?.total_pnl || 0; + const minPnl = minBy(data, (item) => item.total_pnl)?.total_pnl || 0; + const maxCurrentCumulativePnl = + maxBy(data, (item) => item.cumulative_pnl)?.cumulative_pnl || 0; + const minCurrentCumulativePnl = + minBy(data, (item) => item.cumulative_pnl)?.cumulative_pnl || 0; + const stats = { + maxProfit, + maxLoss, + currentProfitCumulative, + currentLossCumulative, + maxCurrentCumulativeProfitLoss: Math.max(currentProfitCumulative, -currentLossCumulative), + minAbsPnl: minPnl, + maxAbsPnl: Math.max(Math.abs(maxPnl), Math.abs(minPnl)), + maxAbsCumulativePnl: Math.max( + Math.abs(maxCurrentCumulativePnl), + Math.abs(minCurrentCumulativePnl) + ), + }; + result = { + data, + stats, + }; + } + return result; + }; - const controls = { - toggles: [ - { - text: "HLP", - event: () => setDataMode('HLP'), - active: dataMode === 'HLP', - }, - { - text: "Liquidator", - event: () => setDataMode('NON_HLP'), - active: dataMode === 'NON_HLP', - } - ] + useEffect(() => { + if ( + dataHLPLiquidatorPNL.length > 0 && + dataCumulativeHLPLiquidatorPNL.length > 0 && + dataNonLiquidator.length > 0 && + dataCumulativeNonLiquidatorPNL.length > 0 + ) { + const formattedTradingData = formatTradingData( + dataHLPLiquidatorPNL, + dataCumulativeHLPLiquidatorPNL + ); + const formattedDataNonLiquidator = formatTradingData( + dataNonLiquidator, + dataCumulativeNonLiquidatorPNL + ); + setChartDataLiquidator(formattedTradingData); + setChartDataNonLiquidator(formattedDataNonLiquidator); } + }, [ + dataHLPLiquidatorPNL, + dataCumulativeHLPLiquidatorPNL, + dataNonLiquidator, + dataCumulativeNonLiquidatorPNL, + ]); + + const controls = { + toggles: [ + { + text: 'HLP', + event: () => setDataMode('HLP'), + active: dataMode === 'HLP', + }, + { + text: 'Liquidator', + event: () => setDataMode('NON_HLP'), + active: dataMode === 'NON_HLP', + }, + ], + }; - const chartInfo = dataMode === 'HLP' ? chartDataLiquidator : chartDataNonLiquidator; - const chartData = chartInfo ? chartInfo.data : []; - const domainYRight = [-chartInfo?.stats.maxAbsCumulativePnl * 1.10, chartInfo?.stats.maxAbsCumulativePnl * 1.10] - const domainYLeft = [-chartInfo?.stats.maxAbsPnl * 1.10, chartInfo?.stats.maxAbsPnl * 1.10] + const chartInfo = dataMode === 'HLP' ? chartDataLiquidator : chartDataNonLiquidator; + const chartData = chartInfo ? chartInfo.data : []; + const domainYRight = [ + -chartInfo?.stats.maxAbsCumulativePnl * 1.1, + chartInfo?.stats.maxAbsCumulativePnl * 1.1, + ]; + const domainYLeft = [-chartInfo?.stats.maxAbsPnl * 1.1, chartInfo?.stats.maxAbsPnl * 1.1]; - return ( - - - - - - - - ''} - contentStyle={{ - textAlign: 'left', - background: "#0A1F1B", - borderColor: "#061412", - boxShadow: "0px 0px 7px rgb(0 0 0 / 20%)", - borderRadius: "26px", - maxHeight: "500px" - }} - itemSorter={(item) => { - return Number(item.value) * -1; - }} - /> - - - {(chartData || []).map((item: any, i: number) => { - return 0 ? GREEN : RED} /> - })} - maxBarSize={20} - - - - - - ) -} \ No newline at end of file + return ( + + + + + + + + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + itemSorter={(item) => { + return Number(item.value) * -1; + }} + /> + + + {(chartData || []).map((item: any, i: number) => { + return 0 ? GREEN : RED} />; + })} + maxBarSize={20} + + + + + + ); +} diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 4ab0b4a..8bd486d 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -1,323 +1,356 @@ import { - Bar, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ComposedChart, - Line, - LineChart + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, + LineChart, } from 'recharts'; import { useEffect, useState } from 'react'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT } from '../../../constants'; import { - CHART_HEIGHT, -} from "../../../constants"; -import { - tooltipFormatter, - tooltipLabelFormatter, - xAxisFormatter, - formatterPercent, - -} from '../../../helpers' -import { getTokenHex } from "../../../constants/tokens"; -import { - liquidity_by_coin, -} from "../../../constants/api" + tooltipFormatter, + tooltipLabelFormatter, + xAxisFormatter, + formatterPercent, +} from '../../../helpers'; +import { getTokenHex } from '../../../constants/tokens'; +import { liquidity_by_coin } from '../../../constants/api'; type DailyUniqueUsersByCoin = { - time: string; - coin: string; - daily_unique_users: number; - percentage_of_total_users: number; - all: number; + time: string; + coin: string; + daily_unique_users: number; + percentage_of_total_users: number; + all: number; }; type UniqueUserTradeData = { - time: string; - daily_unique_users: number; + time: string; + daily_unique_users: number; }; type CumulativeNewUsersData = { - time: string; - daily_new_users: number; - cumulative_new_users: number; + time: string; + daily_new_users: number; + cumulative_new_users: number; }; type GroupedTradeData = { - time: Date; - all: number; - daily_unique_users: number; - cumulative_unique_users: number; - unit: string; - [key: string]: number | Date | { [key: string]: number } | string | undefined; -} + time: Date; + all: number; + daily_unique_users: number; + cumulative_unique_users: number; + unit: string; + [key: string]: number | Date | { [key: string]: number } | string | undefined; +}; type TempGroupedTradeData = { - time: Date; - coins: { [key: string]: number }; - all: number; - daily_unique_users: number; - cumulative_unique_users: number; - unit: string; - [key: string]: number | Date | { [key: string]: number } | string | undefined; -} + time: Date; + coins: { [key: string]: number }; + all: number; + daily_unique_users: number; + cumulative_unique_users: number; + unit: string; + [key: string]: number | Date | { [key: string]: number } | string | undefined; +}; -const REQUESTS = [ - liquidity_by_coin -]; +const REQUESTS = [liquidity_by_coin]; export default function CumulativeUsers() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const [formattedData0, setFormattedData0] = useState([]) - const [formattedData1000, setFormattedData1000] = useState([]) - const [formattedData3000, setFormattedData3000] = useState([]) - const [formattedData10000, setFormattedData10000] = useState([]) - const [minMax, setMinMax] = useState() - - const [coinKeys0, setCoinKeys0] = useState([]) - const [coinKeys1000, setCoinKeys1000] = useState([]) - const [coinKeys3000, setCoinKeys3000] = useState([]) - const [coinKeys10000, setCoinKeys10000] = useState([]) - - - const [dataMode, setDataMode] = useState<'0' | '1000' | '3000' | '10000'>('0') - - const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); - const loading = loadingLiqudity; - const error = errorLiqudity; - - const controls = { - toggles: [ - { - text: "Half Spread", - event: () => setDataMode('0'), - active: dataMode === '0', - }, - { - text: "$1k", - event: () => setDataMode('1000'), - active: dataMode === '1000', - }, - { - text: "$3k", - event: () => setDataMode('3000'), - active: dataMode === '3000', - }, - { - text: "$10k", - event: () => setDataMode('10000'), - active: dataMode === '10000', - } - ] + const [isMobile] = useMediaQuery('(max-width: 700px)'); + + const [formattedData0, setFormattedData0] = useState([]); + const [formattedData1000, setFormattedData1000] = useState([]); + const [formattedData3000, setFormattedData3000] = useState([]); + const [formattedData10000, setFormattedData10000] = useState([]); + const [minMax, setMinMax] = useState(); + + const [coinKeys0, setCoinKeys0] = useState([]); + const [coinKeys1000, setCoinKeys1000] = useState([]); + const [coinKeys3000, setCoinKeys3000] = useState([]); + const [coinKeys10000, setCoinKeys10000] = useState([]); + + const [dataMode, setDataMode] = useState<'0' | '1000' | '3000' | '10000'>('0'); + + const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); + const loading = loadingLiqudity; + const error = errorLiqudity; + + const controls = { + toggles: [ + { + text: 'Half Spread', + event: () => setDataMode('0'), + active: dataMode === '0', + }, + { + text: '$1k', + event: () => setDataMode('1000'), + active: dataMode === '1000', + }, + { + text: '$3k', + event: () => setDataMode('3000'), + active: dataMode === '3000', + }, + { + text: '$10k', + event: () => setDataMode('10000'), + active: dataMode === '10000', + }, + ], + }; + + type InputData = { + [key: string]: { + median_slippage_0: number; + median_slippage_1000: number; + median_slippage_3000: number; + median_slippage_10000: number; + time: string; + }[]; + }; + + type OutputData = { + median_slippage_0: { time: Date; [key: string]: number | Date | string }[]; + median_slippage_1000: { time: Date; [key: string]: number | Date | string }[]; + median_slippage_3000: { time: Date; [key: string]: number | Date | string }[]; + median_slippage_10000: { time: Date; [key: string]: number | Date | string }[]; + }; + + const transformData = (data: InputData): OutputData => { + const coinTotals = new Map(); + + const minMax: { min: number; max: number } = { min: Infinity, max: -Infinity }; + + // Compute overall totals for each coin + for (let key in data) { + data[key].forEach((record) => { + coinTotals.set( + key, + (coinTotals.get(key) || 0) + + record.median_slippage_1000 + + record.median_slippage_3000 + + record.median_slippage_10000 + ); + }); } - type InputData = { - [key: string]: { - median_slippage_0: number, - median_slippage_1000: number, - median_slippage_3000: number, - median_slippage_10000: number, - time: string - }[] - }; - - type OutputData = { - median_slippage_0: { time: Date, [key: string]: number | Date | string }[], - median_slippage_1000: { time: Date, [key: string]: number | Date | string }[], - median_slippage_3000: { time: Date, [key: string]: number | Date | string }[], - median_slippage_10000: { time: Date, [key: string]: number | Date | string }[], - }; - - const transformData = (data: InputData): OutputData => { - const coinTotals = new Map(); - - const minMax: { min: number; max: number } = { min: Infinity, max: -Infinity }; - - // Compute overall totals for each coin - for (let key in data) { - data[key].forEach((record) => { - coinTotals.set(key, (coinTotals.get(key) || 0) + record.median_slippage_1000 + record.median_slippage_3000 + record.median_slippage_10000); - }); - } - - // Get the top 10 coins by total over the whole time period - const topCoins = Array.from(coinTotals.entries()) - .sort((a, b) => b[1] - a[1]) - .slice(0, 10) - .map(([coin]) => coin); - - // Filter data for each category by top 10 coins - const filteredData: InputData = {}; - for (let coin of topCoins) { - filteredData[coin] = data[coin]; - } + // Get the top 10 coins by total over the whole time period + const topCoins = Array.from(coinTotals.entries()) + .sort((a, b) => b[1] - a[1]) + .slice(0, 10) + .map(([coin]) => coin); - const median_slippage_0 = new Map(); - const median_slippage_1000 = new Map(); - const median_slippage_3000 = new Map(); - const median_slippage_10000 = new Map(); - - for (let key in filteredData) { - filteredData[key].forEach((record) => { - const { time, median_slippage_0: val_0, median_slippage_1000: val_1000, median_slippage_3000: val_3000, median_slippage_10000: val_10000 } = record; - - const map0 = median_slippage_0.get(time) || { time: new Date(time), unit: "%" }; - const map1000 = median_slippage_1000.get(time) || { time: new Date(time), unit: "%" }; - const map3000 = median_slippage_3000.get(time) || { time: new Date(time), unit: "%" }; - const map10000 = median_slippage_10000.get(time) || { time: new Date(time), unit: "%" }; - - map0[key] = val_0 * 100; - map1000[key] = val_1000 * 100; - map3000[key] = val_3000 * 100; - map10000[key] = val_10000 * 100; - - median_slippage_0.set(time, map0); - median_slippage_1000.set(time, map1000); - median_slippage_3000.set(time, map3000); - median_slippage_10000.set(time, map10000); - }); - } + // Filter data for each category by top 10 coins + const filteredData: InputData = {}; + for (let coin of topCoins) { + filteredData[coin] = data[coin]; + } - return { - median_slippage_0: Array.from(median_slippage_0.values()), - median_slippage_1000: Array.from(median_slippage_1000.values()), - median_slippage_3000: Array.from(median_slippage_3000.values()), - median_slippage_10000: Array.from(median_slippage_10000.values()) - }; - }; + const median_slippage_0 = new Map< + string, + { time: Date; [key: string]: number | Date | string } + >(); + const median_slippage_1000 = new Map< + string, + { time: Date; [key: string]: number | Date | string } + >(); + const median_slippage_3000 = new Map< + string, + { time: Date; [key: string]: number | Date | string } + >(); + const median_slippage_10000 = new Map< + string, + { time: Date; [key: string]: number | Date | string } + >(); + + for (let key in filteredData) { + filteredData[key].forEach((record) => { + const { + time, + median_slippage_0: val_0, + median_slippage_1000: val_1000, + median_slippage_3000: val_3000, + median_slippage_10000: val_10000, + } = record; + + const map0 = median_slippage_0.get(time) || { time: new Date(time), unit: '%' }; + const map1000 = median_slippage_1000.get(time) || { time: new Date(time), unit: '%' }; + const map3000 = median_slippage_3000.get(time) || { time: new Date(time), unit: '%' }; + const map10000 = median_slippage_10000.get(time) || { time: new Date(time), unit: '%' }; + + map0[key] = val_0 * 100; + map1000[key] = val_1000 * 100; + map3000[key] = val_3000 * 100; + map10000[key] = val_10000 * 100; + + median_slippage_0.set(time, map0); + median_slippage_1000.set(time, map1000); + median_slippage_3000.set(time, map3000); + median_slippage_10000.set(time, map10000); + }); + } - type MinMaxValues = { - min: number; - max: number; + return { + median_slippage_0: Array.from(median_slippage_0.values()), + median_slippage_1000: Array.from(median_slippage_1000.values()), + median_slippage_3000: Array.from(median_slippage_3000.values()), + median_slippage_10000: Array.from(median_slippage_10000.values()), }; - - const getMinMaxValues = (data: any): MinMaxValues => { - let min = Infinity; - let max = -Infinity; - for (let prop in data) { - if (Object.prototype.hasOwnProperty.call(data, prop)) { - const propData = data[prop]; - propData.forEach((item: any) => { - for (let key in item) { - if (key !== 'time' && typeof item[key] === 'number') { - const value = item[key] as number; - min = Math.min(min, value); - max = Math.max(max, value); - } - } - }); + }; + + type MinMaxValues = { + min: number; + max: number; + }; + + const getMinMaxValues = (data: any): MinMaxValues => { + let min = Infinity; + let max = -Infinity; + for (let prop in data) { + if (Object.prototype.hasOwnProperty.call(data, prop)) { + const propData = data[prop]; + propData.forEach((item: any) => { + for (let key in item) { + if (key !== 'time' && typeof item[key] === 'number') { + const value = item[key] as number; + min = Math.min(min, value); + max = Math.max(max, value); } - } - return { min, max }; - }; - - - const extractUniqueCoins = (data: OutputData['median_slippage_1000'] | OutputData['median_slippage_10000'] | OutputData['median_slippage_1000']): string[] => { - const coinSet = new Set(); - data.forEach(record => { - Object.keys(record).forEach(key => { - if (key !== 'time' && key !== 'unit') { - coinSet.add(key); - } - }); + } }); - return Array.from(coinSet); - }; - - const formatData = () => { - const formattedData = transformData(dataLiqudity); - setFormattedData0(formattedData.median_slippage_0) - setFormattedData1000(formattedData.median_slippage_1000) - setFormattedData3000(formattedData.median_slippage_3000) - setFormattedData10000(formattedData.median_slippage_10000) - const formattedUniqueCoinKeys0 = extractUniqueCoins(formattedData.median_slippage_0) - const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000) - const formattedUniqueCoinKeys3000 = extractUniqueCoins(formattedData.median_slippage_3000) - const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000) - const minMaxValues = getMinMaxValues(formattedData); - setMinMax(minMaxValues); - setCoinKeys0(formattedUniqueCoinKeys0) - setCoinKeys1000(formattedUniqueCoinKeys1000) - setCoinKeys3000(formattedUniqueCoinKeys3000) - setCoinKeys10000(formattedUniqueCoinKeys10000) - }; - - useEffect(() => { - if (!loading && !error) { - formatData(); + } + } + return { min, max }; + }; + + const extractUniqueCoins = ( + data: + | OutputData['median_slippage_1000'] + | OutputData['median_slippage_10000'] + | OutputData['median_slippage_1000'] + ): string[] => { + const coinSet = new Set(); + data.forEach((record) => { + Object.keys(record).forEach((key) => { + if (key !== 'time' && key !== 'unit') { + coinSet.add(key); } - }, [loading]) - - const chartData = dataMode === '0' ? formattedData0 : dataMode === '1000' ? formattedData1000 : dataMode === '3000' ? formattedData3000 : formattedData10000; - - const chartDataCoinKeys = dataMode === '0' ? coinKeys0 : dataMode === '1000' ? coinKeys1000 : dataMode === '3000' ? coinKeys3000 : coinKeys10000; - - return ( - - - - - - - { - return Number(item.value) * -1; - }} - /> - - { - chartDataCoinKeys.map(((coinName, i) => { - return ( - - ) - })) - } - - - - Top 10 Coins over time - - - ) -} \ No newline at end of file + }); + }); + return Array.from(coinSet); + }; + + const formatData = () => { + const formattedData = transformData(dataLiqudity); + setFormattedData0(formattedData.median_slippage_0); + setFormattedData1000(formattedData.median_slippage_1000); + setFormattedData3000(formattedData.median_slippage_3000); + setFormattedData10000(formattedData.median_slippage_10000); + const formattedUniqueCoinKeys0 = extractUniqueCoins(formattedData.median_slippage_0); + const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000); + const formattedUniqueCoinKeys3000 = extractUniqueCoins(formattedData.median_slippage_3000); + const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000); + const minMaxValues = getMinMaxValues(formattedData); + setMinMax(minMaxValues); + setCoinKeys0(formattedUniqueCoinKeys0); + setCoinKeys1000(formattedUniqueCoinKeys1000); + setCoinKeys3000(formattedUniqueCoinKeys3000); + setCoinKeys10000(formattedUniqueCoinKeys10000); + }; + + useEffect(() => { + if (!loading && !error) { + formatData(); + } + }, [loading]); + + const chartData = + dataMode === '0' + ? formattedData0 + : dataMode === '1000' + ? formattedData1000 + : dataMode === '3000' + ? formattedData3000 + : formattedData10000; + + const chartDataCoinKeys = + dataMode === '0' + ? coinKeys0 + : dataMode === '1000' + ? coinKeys1000 + : dataMode === '3000' + ? coinKeys3000 + : coinKeys10000; + + return ( + + + + + + + { + return Number(item.value) * -1; + }} + /> + + {chartDataCoinKeys.map((coinName, i) => { + return ( + + ); + })} + + + + Top 10 Coins over time + + + ); +} diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index c7acb2f..c91315a 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -1,193 +1,191 @@ import { - Bar, - Label, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - LineChart, - ResponsiveContainer, - ComposedChart, - Line + Bar, + Label, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + LineChart, + ResponsiveContainer, + ComposedChart, + Line, } from 'recharts'; import { useEffect, useState } from 'react'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT, YAXIS_WIDTH } from '../../../constants'; import { - CHART_HEIGHT, - YAXIS_WIDTH, -} from "../../../constants"; -import { - xAxisFormatter, - tooltipLabelFormatter, - yaxisFormatter, - tooltipFormatterCurrency, -} from '../../../helpers' -import { getTokenHex } from "../../../constants/tokens"; -import { - open_interest, - -} from "../../../constants/api" + xAxisFormatter, + tooltipLabelFormatter, + yaxisFormatter, + tooltipFormatterCurrency, +} from '../../../helpers'; +import { getTokenHex } from '../../../constants/tokens'; +import { open_interest } from '../../../constants/api'; type OpenInterestData = { time: string; coin: string; open_interest: number }; -type GroupedOpenInterestData = { time: string;[key: string]: number | string }; +type GroupedOpenInterestData = { time: string; [key: string]: number | string }; -const REQUESTS = [ - open_interest -]; +const REQUESTS = [open_interest]; export default function VolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const [coinKeys, setCoinKeys] = useState([]) - const [formattedData, setFormattedData] = useState([]) - const [dataOpenInterest, loadingOpenInterest, errorOpenInterest] = useRequest(REQUESTS[0], [], 'chart_data'); - - const loading = loadingOpenInterest; - const error = errorOpenInterest; - - type OpenInterestData = { time: string; coin: string; open_interest: number }; - type GroupedOpenInterestData = { time: Date; unit: string; all: number;[key: string]: number | Date | string }; - - const groupByTime = (data: OpenInterestData[]): GroupedOpenInterestData[] => { - const map = new Map(); - const totalOpenInterestMap = new Map(); - - data.forEach((item) => { - const key = item.time; - if (!map.has(key)) { - map.set(key, { - time: new Date(key), - Other: 0, // Initialize the 'Other' property - all: 0, // Initialize the 'all' property - unit: "$", // Initialize the 'unit' property - }); - } - const existingEntry = map.get(key); - existingEntry[item.coin] = (existingEntry[item.coin] || 0) + item.open_interest; - existingEntry.all += item.open_interest; // Aggregate total open interest for 'all' property - - // Aggregate total open interest for each coin - totalOpenInterestMap.set(item.coin, (totalOpenInterestMap.get(item.coin) || 0) + item.open_interest); - }); - - // Get top 10 coins by total open interest - const top10Coins = Array.from(totalOpenInterestMap.entries()) - .sort(([, a], [, b]) => b - a) - .slice(0, 10) - .map(([coin]) => coin); - - // Filter out the coins that are not in the top 10 and calculate 'Other' - return Array.from(map.values()).map((record: any) => { - let otherSum = 0; - Object.keys(record).forEach((coin) => { - if (coin !== 'time' && coin !== 'unit' && coin !== 'all' && !top10Coins.includes(coin)) { // Exclude 'unit' and 'all' from calculations - otherSum += record[coin]; - delete record[coin]; - } - }); - record.Other = otherSum; // Update 'Other' property with sum of other coins - return record as GroupedOpenInterestData; + const [isMobile] = useMediaQuery('(max-width: 700px)'); + + const [coinKeys, setCoinKeys] = useState([]); + const [formattedData, setFormattedData] = useState([]); + const [dataOpenInterest, loadingOpenInterest, errorOpenInterest] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + + const loading = loadingOpenInterest; + const error = errorOpenInterest; + + type OpenInterestData = { time: string; coin: string; open_interest: number }; + type GroupedOpenInterestData = { + time: Date; + unit: string; + all: number; + [key: string]: number | Date | string; + }; + + const groupByTime = (data: OpenInterestData[]): GroupedOpenInterestData[] => { + const map = new Map(); + const totalOpenInterestMap = new Map(); + + data.forEach((item) => { + const key = item.time; + if (!map.has(key)) { + map.set(key, { + time: new Date(key), + Other: 0, // Initialize the 'Other' property + all: 0, // Initialize the 'all' property + unit: '$', // Initialize the 'unit' property }); - }; - - const extractUniqueCoins = (formattedVolumeData: GroupedOpenInterestData[]): string[] => { - const coinSet = new Set(); - for (const data of formattedVolumeData) { - Object.keys(data).forEach(coin => { - if (coin !== 'all' && - coin !== 'time' && - coin !== 'unit' - ) { - coinSet.add(coin); - } - }); + } + const existingEntry = map.get(key); + existingEntry[item.coin] = (existingEntry[item.coin] || 0) + item.open_interest; + existingEntry.all += item.open_interest; // Aggregate total open interest for 'all' property + + // Aggregate total open interest for each coin + totalOpenInterestMap.set( + item.coin, + (totalOpenInterestMap.get(item.coin) || 0) + item.open_interest + ); + }); + + // Get top 10 coins by total open interest + const top10Coins = Array.from(totalOpenInterestMap.entries()) + .sort(([, a], [, b]) => b - a) + .slice(0, 10) + .map(([coin]) => coin); + + // Filter out the coins that are not in the top 10 and calculate 'Other' + return Array.from(map.values()).map((record: any) => { + let otherSum = 0; + Object.keys(record).forEach((coin) => { + if (coin !== 'time' && coin !== 'unit' && coin !== 'all' && !top10Coins.includes(coin)) { + // Exclude 'unit' and 'all' from calculations + otherSum += record[coin]; + delete record[coin]; } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); + }); + record.Other = otherSum; // Update 'Other' property with sum of other coins + return record as GroupedOpenInterestData; + }); + }; + + const extractUniqueCoins = (formattedVolumeData: GroupedOpenInterestData[]): string[] => { + const coinSet = new Set(); + for (const data of formattedVolumeData) { + Object.keys(data).forEach((coin) => { + if (coin !== 'all' && coin !== 'time' && coin !== 'unit') { + coinSet.add(coin); } - return coinsArray; - }; - - const formatData = () => { - const groupedData = groupByTime(dataOpenInterest); - const uniqueCoins = extractUniqueCoins(groupedData); - setFormattedData(groupedData); - setCoinKeys(uniqueCoins); + }); } - - useEffect(() => { - if (!loading && !error) { - formatData(); - } - }, [loading]) - - return ( - - - - - - - { - return Number(item.value) * -1; - }} - /> - - { - coinKeys.map(((coinName, i) => { - return ( - - ) - })) - } - - - - Top 10 Coins grouped by total volume over time and remaining coins grouped by Other - - - ) -} \ No newline at end of file + const coinsArray = Array.from(coinSet); + if (coinsArray.includes('Other')) { + const index = coinsArray.indexOf('Other'); + coinsArray.splice(index, 1); + coinsArray.push('Other'); + } + return coinsArray; + }; + + const formatData = () => { + const groupedData = groupByTime(dataOpenInterest); + const uniqueCoins = extractUniqueCoins(groupedData); + setFormattedData(groupedData); + setCoinKeys(uniqueCoins); + }; + + useEffect(() => { + if (!loading && !error) { + formatData(); + } + }, [loading]); + + return ( + + + + + + + { + return Number(item.value) * -1; + }} + /> + + {coinKeys.map((coinName, i) => { + return ( + + ); + })} + + + + + Top 10 Coins grouped by total volume over time and remaining coins grouped by Other + + + + ); +} diff --git a/components/home/charts/top-stats.tsx b/components/home/charts/top-stats.tsx index 14fdbf9..2ee2b3c 100644 --- a/components/home/charts/top-stats.tsx +++ b/components/home/charts/top-stats.tsx @@ -1,97 +1,134 @@ -'use strict' +'use strict'; import React from 'react'; import { Box, Grid, Text, Spinner } from '@chakra-ui/react'; -import { total_users, total_usd_volume, total_deposits, total_withdrawals, total_notional_liquidated } from "../../../constants/api" +import { + total_users, + total_usd_volume, + total_deposits, + total_withdrawals, + total_notional_liquidated, +} from '../../../constants/api'; import { useRequest } from '@/hooks/useRequest'; import { formatNumber } from '@/utils/formatting'; const REQUESTS = [ - total_users, - total_usd_volume, - total_deposits, - total_withdrawals, - total_notional_liquidated, + total_users, + total_usd_volume, + total_deposits, + total_withdrawals, + total_notional_liquidated, ]; -const Card = (props: any) => ( - -); +const Card = (props: any) => ; -const Loader = () => +const Loader = () => ( + + + +); const TopStats = () => { + const [dataTotalUsers, loadingTotalUsers, errorTotalUsers] = useRequest( + REQUESTS[0], + 0, + 'total_users', + true + ); + const [dataTotalUsdVol, loadingUsdVol, errorUsdVol] = useRequest( + REQUESTS[1], + 0, + 'total_usd_volume', + true + ); + const [dataTotalDeposits, loadingTotalDeposits, errorTotalDeposits] = useRequest( + REQUESTS[2], + 0, + 'total_deposits', + true + ); + const [dataTotalWithdrawals, loadingTotalWithdrawals, errorTotalWithdrawals] = useRequest( + REQUESTS[3], + 0, + 'total_withdrawals', + true + ); + const [ + dataTotalNotionalLiquidated, + loadingTotalNotionalLiquidated, + errorTotalNotionalLiquidated, + ] = useRequest(REQUESTS[4], 0, 'total_notional_liquidated', true); - const [dataTotalUsers, loadingTotalUsers, errorTotalUsers] = useRequest(REQUESTS[0], 0, "total_users", true); - const [dataTotalUsdVol, loadingUsdVol, errorUsdVol] = useRequest(REQUESTS[1], 0, "total_usd_volume", true); - const [dataTotalDeposits, loadingTotalDeposits, errorTotalDeposits] = useRequest(REQUESTS[2], 0, "total_deposits", true); - const [dataTotalWithdrawals, loadingTotalWithdrawals, errorTotalWithdrawals] = useRequest(REQUESTS[3], 0, "total_withdrawals", true); - const [dataTotalNotionalLiquidated, loadingTotalNotionalLiquidated, errorTotalNotionalLiquidated] = useRequest(REQUESTS[4], 0, "total_notional_liquidated", true); - - return ( - - - - {dataTotalUsers ? formatNumber(dataTotalUsers) : errorTotalUsers ? "Error" : null} - - - {loadingTotalUsers && } - - - - {dataTotalUsdVol ? `$${formatNumber(dataTotalUsdVol, 0)}` : errorUsdVol ? "Error" : null} - - - {loadingUsdVol && } - - - - {dataTotalDeposits ? `$${formatNumber(dataTotalDeposits, 0)}` : errorTotalDeposits ? "Error" : null} - - - {loadingTotalDeposits && } - - - - {dataTotalWithdrawals ? `$${formatNumber(Math.abs(dataTotalWithdrawals), 0)}` : errorTotalWithdrawals ? "Error" : null} - - - {loadingTotalWithdrawals && } - - - - {dataTotalNotionalLiquidated ? `$${formatNumber(dataTotalNotionalLiquidated, 0)}` : errorTotalNotionalLiquidated ? "Error" : null} - - - {loadingTotalNotionalLiquidated && } - - - ); + return ( + + + + {dataTotalUsers ? formatNumber(dataTotalUsers) : errorTotalUsers ? 'Error' : null} + + + {loadingTotalUsers && } + + + + {dataTotalUsdVol ? `$${formatNumber(dataTotalUsdVol, 0)}` : errorUsdVol ? 'Error' : null} + + + {loadingUsdVol && } + + + + {dataTotalDeposits + ? `$${formatNumber(dataTotalDeposits, 0)}` + : errorTotalDeposits + ? 'Error' + : null} + + + {loadingTotalDeposits && } + + + + {dataTotalWithdrawals + ? `$${formatNumber(Math.abs(dataTotalWithdrawals), 0)}` + : errorTotalWithdrawals + ? 'Error' + : null} + + + {loadingTotalWithdrawals && } + + + + {dataTotalNotionalLiquidated + ? `$${formatNumber(dataTotalNotionalLiquidated, 0)}` + : errorTotalNotionalLiquidated + ? 'Error' + : null} + + + {loadingTotalNotionalLiquidated && } + + + ); }; export default TopStats; diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index 9d89b12..83455e6 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -1,173 +1,172 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react'; import { - Area, - Bar, - Cell, - CartesianGrid, - ComposedChart, - Legend, - Line, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis + Area, + Bar, + Cell, + CartesianGrid, + ComposedChart, + Legend, + Line, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, } from 'recharts'; -import { chain, sumBy, sortBy, maxBy, minBy } from 'lodash' +import { chain, sumBy, sortBy, maxBy, minBy } from 'lodash'; import { useRequest } from '@/hooks/useRequest'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" -import { cumulative_user_pnl, user_pnl } from "../../../constants/api" +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; +import { cumulative_user_pnl, user_pnl } from '../../../constants/api'; import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; import { - CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, - GREEN, - RED, -} from "../../../constants"; -import { - yaxisFormatter, - xAxisFormatter, - formatNumberWithOptions, - tooltipFormatterCurrency, -} from '../../../helpers' + yaxisFormatter, + xAxisFormatter, + formatNumberWithOptions, + tooltipFormatterCurrency, +} from '../../../helpers'; -const REQUESTS = [ - cumulative_user_pnl, - user_pnl, -]; +const REQUESTS = [cumulative_user_pnl, user_pnl]; export default function TradersProfitLossChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [data, setData] = useState(null); - const [dataCumulativeUserPNL, loadingCumulativeUserPNL, errorCumulativeUserPNL] = useRequest(REQUESTS[0], [], 'chart_data'); - const [dataUserPNL, loadingUserPNL, errorUserPNL] = useRequest(REQUESTS[1], [], 'chart_data'); + const [data, setData] = useState(null); + const [dataCumulativeUserPNL, loadingCumulativeUserPNL, errorCumulativeUserPNL] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + const [dataUserPNL, loadingUserPNL, errorUserPNL] = useRequest(REQUESTS[1], [], 'chart_data'); - const formatTradingData = () => { - let currentPnlCumulative = 0; - let currentProfitCumulative = 0; - let currentLossCumulative = 0; + const formatTradingData = () => { + let currentPnlCumulative = 0; + let currentProfitCumulative = 0; + let currentLossCumulative = 0; - dataUserPNL.shift(); - dataCumulativeUserPNL.shift(); + dataUserPNL.shift(); + dataCumulativeUserPNL.shift(); - if (!dataCumulativeUserPNL || !dataCumulativeUserPNL.length || !dataUserPNL || !dataUserPNL.length) return + if ( + !dataCumulativeUserPNL || + !dataCumulativeUserPNL.length || + !dataUserPNL || + !dataUserPNL.length + ) + return; - let data: any[] = sortBy(dataCumulativeUserPNL, (i: any) => Date.parse(i.time)).map((dataItem: any, index: number) => ({ - cumulative_pnl: dataItem.cumulative_pnl, - user_pnl: dataUserPNL && dataUserPNL[index] ? dataUserPNL[index].total_pnl : 0, - timestamp: new Date(dataItem.time), - profit_cumulative: dataItem.cumulative_pnl > 0 ? dataItem.cumulative_pnl : 0, - loss_cumulative: dataItem.cumulative_pnl < 0 ? dataItem.cumulative_pnl : 0, - unit: "single", - })); + let data: any[] = sortBy(dataCumulativeUserPNL, (i: any) => Date.parse(i.time)).map( + (dataItem: any, index: number) => ({ + cumulative_pnl: dataItem.cumulative_pnl, + user_pnl: dataUserPNL && dataUserPNL[index] ? dataUserPNL[index].total_pnl : 0, + timestamp: new Date(dataItem.time), + profit_cumulative: dataItem.cumulative_pnl > 0 ? dataItem.cumulative_pnl : 0, + loss_cumulative: dataItem.cumulative_pnl < 0 ? dataItem.cumulative_pnl : 0, + unit: 'single', + }) + ); - const maxProfit = maxBy(data, item => item.profit_cumulative).profit_cumulative - const maxLoss = minBy(data, item => item.loss_cumulative).loss_cumulative + const maxProfit = maxBy(data, (item) => item.profit_cumulative).profit_cumulative; + const maxLoss = minBy(data, (item) => item.loss_cumulative).loss_cumulative; - const maxPnl = maxBy(data, item => item.user_pnl).user_pnl; - const minPnl = minBy(data, item => item.user_pnl).user_pnl; + const maxPnl = maxBy(data, (item) => item.user_pnl).user_pnl; + const minPnl = minBy(data, (item) => item.user_pnl).user_pnl; - const maxCurrentCumulativePnl = maxBy(data, item => item.cumulative_pnl).cumulative_pnl; - const minCurrentCumulativePnl = minBy(data, item => item.cumulative_pnl).cumulative_pnl; + const maxCurrentCumulativePnl = maxBy(data, (item) => item.cumulative_pnl).cumulative_pnl; + const minCurrentCumulativePnl = minBy(data, (item) => item.cumulative_pnl).cumulative_pnl; - const stats = { - maxProfit, - maxLoss, - currentProfitCumulative, - currentLossCumulative, - maxCurrentCumulativeProfitLoss: Math.max(currentProfitCumulative, -currentLossCumulative), + const stats = { + maxProfit, + maxLoss, + currentProfitCumulative, + currentLossCumulative, + maxCurrentCumulativeProfitLoss: Math.max(currentProfitCumulative, -currentLossCumulative), - maxAbsPnl: Math.max( - Math.abs(maxPnl), - Math.abs(minPnl), - ), - maxAbsCumulativePnl: Math.max( - Math.abs(maxCurrentCumulativePnl), - Math.abs(minCurrentCumulativePnl) - ), - } + maxAbsPnl: Math.max(Math.abs(maxPnl), Math.abs(minPnl)), + maxAbsCumulativePnl: Math.max( + Math.abs(maxCurrentCumulativePnl), + Math.abs(minCurrentCumulativePnl) + ), + }; - setData({ - data, - stats, - }) - } + setData({ + data, + stats, + }); + }; - useEffect(() => { - if (dataCumulativeUserPNL.length > 0 && dataUserPNL.length > 0) { - formatTradingData(); - } - }, [dataCumulativeUserPNL, dataUserPNL]); + useEffect(() => { + if (dataCumulativeUserPNL.length > 0 && dataUserPNL.length > 0) { + formatTradingData(); + } + }, [dataCumulativeUserPNL, dataUserPNL]); - return ( - - - - - - - - ''} - contentStyle={{ - textAlign: 'left', - background: "#0A1F1B", - borderColor: "#061412", - color: "#fff", - boxShadow: "0px 0px 7px rgb(0 0 0 / 20%)", - borderRadius: "26px", - maxHeight: "500px" - }} - itemSorter={(item) => { - return Number(item.value) * -1; - }} - /> - - - {(data && data.data || []).map((item: any, i: number) => { - return 0 ? GREEN : RED} /> - })} - maxBarSize={20} - - - - - - - This is computed as negative of (HLP + liquidator) - - - - ) -} \ No newline at end of file + return ( + + + + + + + + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + color: '#fff', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + itemSorter={(item) => { + return Number(item.value) * -1; + }} + /> + + + {((data && data.data) || []).map((item: any, i: number) => { + return 0 ? GREEN : RED} />; + })} + maxBarSize={20} + + + + + + This is computed as negative of (HLP + liquidator) + + + ); +} diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 3c192b3..75b05b2 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -1,265 +1,278 @@ import { - Bar, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ComposedChart, - Line + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, } from 'recharts'; import { useEffect, useState } from 'react'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; import { - CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, -} from "../../../constants"; + tooltipFormatter, + tooltipLabelFormatter, + xAxisFormatter, + yaxisFormatterNumber, + yaxisFormatterPercent, +} from '../../../helpers'; +import { getTokenHex } from '../../../constants/tokens'; import { - tooltipFormatter, - tooltipLabelFormatter, - xAxisFormatter, - yaxisFormatterNumber, - yaxisFormatterPercent, -} from '../../../helpers' -import { getTokenHex } from "../../../constants/tokens"; -import { - cumulative_new_users, - daily_unique_users, - daily_unique_users_by_coin, -} from "../../../constants/api" + cumulative_new_users, + daily_unique_users, + daily_unique_users_by_coin, +} from '../../../constants/api'; type DailyUniqueUsersByCoin = { - time: string; - coin: string; - daily_unique_users: number; - percentage_of_total_users: number; - all: number; + time: string; + coin: string; + daily_unique_users: number; + percentage_of_total_users: number; + all: number; }; type UniqueUserTradeData = { - time: string; - daily_unique_users: number; + time: string; + daily_unique_users: number; }; type CumulativeNewUsersData = { - time: string; - daily_new_users: number; - cumulative_new_users: number; + time: string; + daily_new_users: number; + cumulative_new_users: number; }; type GroupedTradeData = { - time: Date; - daily_unique_users: number; - cumulative_unique_users: number; - unit: string; - [key: string]: number | Date | { [key: string]: number } | string | undefined; -} + time: Date; + daily_unique_users: number; + cumulative_unique_users: number; + unit: string; + [key: string]: number | Date | { [key: string]: number } | string | undefined; +}; type TempGroupedTradeData = { - time: Date; - coins: { [key: string]: number }; - //all: number; - daily_unique_users: number; - cumulative_unique_users: number; - unit: string; - [key: string]: number | Date | { [key: string]: number } | string | undefined; -} + time: Date; + coins: { [key: string]: number }; + //all: number; + daily_unique_users: number; + cumulative_unique_users: number; + unit: string; + [key: string]: number | Date | { [key: string]: number } | string | undefined; +}; -const REQUESTS = [ - cumulative_new_users, - daily_unique_users, - daily_unique_users_by_coin, -]; +const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; export default function CumulativeUsers() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [formattedData, setFormattedData] = useState([]) - const [coinKeys, setCoinKeys] = useState([]) + const [formattedData, setFormattedData] = useState([]); + const [coinKeys, setCoinKeys] = useState([]); - const [dataCumulativeNewUsers, loadingCumulativeNewUsers, errorCumulativeNewUsers] = useRequest(REQUESTS[0], [], 'chart_data'); - const [dataDailyUniqueUsers, loadingDailyUniqueUsers, errorDailyUniqueUsers] = useRequest(REQUESTS[1], [], 'chart_data'); - const [dataDailyUniqueUsersByCoin, loadingDailyUniqueUsersByCoin, errorDailyUniqueUsersByCoin] = useRequest(REQUESTS[2], [], 'chart_data'); + const [dataCumulativeNewUsers, loadingCumulativeNewUsers, errorCumulativeNewUsers] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + const [dataDailyUniqueUsers, loadingDailyUniqueUsers, errorDailyUniqueUsers] = useRequest( + REQUESTS[1], + [], + 'chart_data' + ); + const [dataDailyUniqueUsersByCoin, loadingDailyUniqueUsersByCoin, errorDailyUniqueUsersByCoin] = + useRequest(REQUESTS[2], [], 'chart_data'); - const loading = loadingCumulativeNewUsers || loadingDailyUniqueUsers || loadingDailyUniqueUsersByCoin; - const error = errorCumulativeNewUsers || errorDailyUniqueUsers || errorDailyUniqueUsersByCoin; + const loading = + loadingCumulativeNewUsers || loadingDailyUniqueUsers || loadingDailyUniqueUsersByCoin; + const error = errorCumulativeNewUsers || errorDailyUniqueUsers || errorDailyUniqueUsersByCoin; - const formatTradesByCoinAndTime = ( - dataDailyUniqueUsersByCoin: DailyUniqueUsersByCoin[], - uniqueUserTradeData: UniqueUserTradeData[], - dataCumulativeNewUsers: CumulativeNewUsersData[] - ): GroupedTradeData[] => { - const temp: { [key: string]: TempGroupedTradeData } = {}; - const uniqueUserTradeDataMap: { [key: string]: number } = {}; - const cumulativeUniqueUserTradeDataMap: { [key: string]: number } = {}; + const formatTradesByCoinAndTime = ( + dataDailyUniqueUsersByCoin: DailyUniqueUsersByCoin[], + uniqueUserTradeData: UniqueUserTradeData[], + dataCumulativeNewUsers: CumulativeNewUsersData[] + ): GroupedTradeData[] => { + const temp: { [key: string]: TempGroupedTradeData } = {}; + const uniqueUserTradeDataMap: { [key: string]: number } = {}; + const cumulativeUniqueUserTradeDataMap: { [key: string]: number } = {}; - uniqueUserTradeData.forEach(item => { - uniqueUserTradeDataMap[item.time] = item.daily_unique_users; - }); + uniqueUserTradeData.forEach((item) => { + uniqueUserTradeDataMap[item.time] = item.daily_unique_users; + }); - dataCumulativeNewUsers.forEach(item => { - cumulativeUniqueUserTradeDataMap[item.time] = item.cumulative_new_users; - }); + dataCumulativeNewUsers.forEach((item) => { + cumulativeUniqueUserTradeDataMap[item.time] = item.cumulative_new_users; + }); - dataDailyUniqueUsersByCoin.forEach(({ time, coin, daily_unique_users, percentage_of_total_users }) => { - if (!temp[time]) { - temp[time] = { - time: new Date(time), - coins: {}, - daily_unique_users: uniqueUserTradeDataMap[time] || 0, - cumulative_unique_users: cumulativeUniqueUserTradeDataMap[time] || 0, - unit: '%' - }; - } - temp[time].coins[coin] = (temp[time].coins[coin] || 0) + (percentage_of_total_users * 100); - temp[time][`${coin}_daily_unique_users`] = daily_unique_users; - }); + dataDailyUniqueUsersByCoin.forEach( + ({ time, coin, daily_unique_users, percentage_of_total_users }) => { + if (!temp[time]) { + temp[time] = { + time: new Date(time), + coins: {}, + daily_unique_users: uniqueUserTradeDataMap[time] || 0, + cumulative_unique_users: cumulativeUniqueUserTradeDataMap[time] || 0, + unit: '%', + }; + } + temp[time].coins[coin] = (temp[time].coins[coin] || 0) + percentage_of_total_users * 100; + temp[time][`${coin}_daily_unique_users`] = daily_unique_users; + } + ); - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj) - .sort(([, aVolume], [, bVolume]) => bVolume - aVolume); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); - const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); - return { - ...Object.fromEntries(top10Entries), - Other: otherVolume - }; - }; + const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { + const sortedEntries = Object.entries(obj).sort( + ([, aVolume], [, bVolume]) => bVolume - aVolume + ); + const top10Entries = sortedEntries.slice(0, 10); + const otherEntries = sortedEntries.slice(10); + const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); + return { + ...Object.fromEntries(top10Entries), + Other: otherVolume, + }; + }; - return Object.values(temp).map(({ time, coins, ...rest }) => { - return { - time: new Date(time), - ...sortAndSliceTop10(coins), - ...rest, - unit: '%', - }; - }); - } + return Object.values(temp).map(({ time, coins, ...rest }) => { + return { + time: new Date(time), + ...sortAndSliceTop10(coins), + ...rest, + unit: '%', + }; + }); + }; - const extractUniqueCoins = (formattedVolumeData: GroupedTradeData[]): string[] => { - const coinSet = new Set(); - for (const data of formattedVolumeData) { - Object.keys(data).forEach(coin => { - if (coin !== 'all' && - coin !== 'cumulative' && - coin !== 'time' && - coin !== 'other' && - coin !== 'unit' && - coin !== 'daily_unique_users' && - coin !== 'cumulative_unique_users' && - !coin.includes('daily_unique_users') - ) { - coinSet.add(coin); - } - }); + const extractUniqueCoins = (formattedVolumeData: GroupedTradeData[]): string[] => { + const coinSet = new Set(); + for (const data of formattedVolumeData) { + Object.keys(data).forEach((coin) => { + if ( + coin !== 'all' && + coin !== 'cumulative' && + coin !== 'time' && + coin !== 'other' && + coin !== 'unit' && + coin !== 'daily_unique_users' && + coin !== 'cumulative_unique_users' && + !coin.includes('daily_unique_users') + ) { + coinSet.add(coin); } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } - return coinsArray; - }; + }); + } + const coinsArray = Array.from(coinSet); + if (coinsArray.includes('Other')) { + const index = coinsArray.indexOf('Other'); + coinsArray.splice(index, 1); + coinsArray.push('Other'); + } + return coinsArray; + }; - const formatData = () => { - const formattedData = formatTradesByCoinAndTime(dataDailyUniqueUsersByCoin, dataDailyUniqueUsers, dataCumulativeNewUsers); - const formattedUniqueCoinKeys = extractUniqueCoins(formattedData) - setFormattedData(formattedData); - setCoinKeys(formattedUniqueCoinKeys) - }; + const formatData = () => { + const formattedData = formatTradesByCoinAndTime( + dataDailyUniqueUsersByCoin, + dataDailyUniqueUsers, + dataCumulativeNewUsers + ); + const formattedUniqueCoinKeys = extractUniqueCoins(formattedData); + setFormattedData(formattedData); + setCoinKeys(formattedUniqueCoinKeys); + }; - useEffect(() => { - if (!loading && !error) { - formatData(); - } - }, [loading]) + useEffect(() => { + if (!loading && !error) { + formatData(); + } + }, [loading]); - return ( - - - - - - - - { - return Number(item.value) * -1; - }} - /> - - { - coinKeys.map(((coinName, i) => { - return ( - - ) - })) - } - - - - - - The line is the number of unique addresses who used Hyperliquid each day, bars represent proportion of users who traded specific coins. Total exceeds 100% as users can trade multiple coins. Top 10 coins are shown separately and the rest are grouped as Other. - - - - ) -} \ No newline at end of file + return ( + + + + + + + + { + return Number(item.value) * -1; + }} + /> + + {coinKeys.map((coinName, i) => { + return ( + + ); + })} + + + + + + The line is the number of unique addresses who used Hyperliquid each day, bars represent + proportion of users who traded specific coins. Total exceeds 100% as users can trade + multiple coins. Top 10 coins are shown separately and the rest are grouped as Other. + + + + ); +} diff --git a/components/home/charts/volume-non-hlp.tsx b/components/home/charts/volume-non-hlp.tsx index a9f8936..1c635fb 100644 --- a/components/home/charts/volume-non-hlp.tsx +++ b/components/home/charts/volume-non-hlp.tsx @@ -1,336 +1,369 @@ import { - Bar, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ComposedChart, - Line + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, } from 'recharts'; import { useEffect, useState } from 'react'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; import { - CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, - BRAND_GREEN_2, - BRAND_GREEN_3, -} from "../../../constants"; + CHART_HEIGHT, + YAXIS_WIDTH, + BRIGHT_GREEN, + BRAND_GREEN_2, + BRAND_GREEN_3, +} from '../../../constants'; import { - tooltipFormatterCurrency, - tooltipLabelFormatter, - yaxisFormatter, - xAxisFormatter, -} from '../../../helpers' -import { getTokenHex } from "../../../constants/tokens"; + tooltipFormatterCurrency, + tooltipLabelFormatter, + yaxisFormatter, + xAxisFormatter, +} from '../../../helpers'; +import { getTokenHex } from '../../../constants/tokens'; import { - cumulative_usd_volume, - daily_usd_volume, - daily_usd_volume_by_coin, - daily_usd_volume_by_crossed, - daily_usd_volume_by_user, -} from "../../../constants/api" + cumulative_usd_volume, + daily_usd_volume, + daily_usd_volume_by_coin, + daily_usd_volume_by_crossed, + daily_usd_volume_by_user, +} from '../../../constants/api'; const REQUESTS = [ - cumulative_usd_volume, - daily_usd_volume, - daily_usd_volume_by_coin, - daily_usd_volume_by_crossed, - daily_usd_volume_by_user, + cumulative_usd_volume, + daily_usd_volume, + daily_usd_volume_by_coin, + daily_usd_volume_by_crossed, + daily_usd_volume_by_user, ]; export default function VolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<"COINS" | "MARGIN">('COINS'); - const [formattedDataCoins, setFormattedDataCoins] = useState([]) - const [formattedDataMarin, setFormattedDataMarin] = useState([]) - const [formattedCumulativeVolumeData, setFormattedCumulativeVolumeData] = useState([]); - const [coinKeys, setCoinKeys] = useState([]) - const [dataCumulativeUsdVolume, loadingCumulativeUsdVolume, errorCumulativeUsdVolume] = useRequest(REQUESTS[0], [], 'chart_data'); - const [dataDailyUsdVolume, loadingDailyUsdVolume, errorDailyUsdVolume] = useRequest(REQUESTS[1], [], 'chart_data'); - const [dataDailyUsdVolumeByCoin, loadingDailyUsdVolumeByCoin, errorDailyUsdVolumeByCoin] = useRequest(REQUESTS[2], [], 'chart_data'); - const [dataDailyUsdVolumeByCrossed, loadingDailyUsdVolumeByCrossed, errorDailyUsdVolumeByCrossed] = useRequest(REQUESTS[3], [], 'chart_data'); - const [dataDailyUsdVolumeByUser, loadingDailyUsdVolumeByUser, errorDailyUsdVolumeByUser] = useRequest(REQUESTS[4], [], 'chart_data'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); + const [formattedDataCoins, setFormattedDataCoins] = useState([]); + const [formattedDataMarin, setFormattedDataMarin] = useState([]); + const [formattedCumulativeVolumeData, setFormattedCumulativeVolumeData] = useState([]); + const [coinKeys, setCoinKeys] = useState([]); + const [dataCumulativeUsdVolume, loadingCumulativeUsdVolume, errorCumulativeUsdVolume] = + useRequest(REQUESTS[0], [], 'chart_data'); + const [dataDailyUsdVolume, loadingDailyUsdVolume, errorDailyUsdVolume] = useRequest( + REQUESTS[1], + [], + 'chart_data' + ); + const [dataDailyUsdVolumeByCoin, loadingDailyUsdVolumeByCoin, errorDailyUsdVolumeByCoin] = + useRequest(REQUESTS[2], [], 'chart_data'); + const [ + dataDailyUsdVolumeByCrossed, + loadingDailyUsdVolumeByCrossed, + errorDailyUsdVolumeByCrossed, + ] = useRequest(REQUESTS[3], [], 'chart_data'); + const [dataDailyUsdVolumeByUser, loadingDailyUsdVolumeByUser, errorDailyUsdVolumeByUser] = + useRequest(REQUESTS[4], [], 'chart_data'); - const loading = loadingCumulativeUsdVolume || loadingDailyUsdVolume || loadingDailyUsdVolumeByCoin || loadingDailyUsdVolumeByCrossed || loadingDailyUsdVolumeByUser; + const loading = + loadingCumulativeUsdVolume || + loadingDailyUsdVolume || + loadingDailyUsdVolumeByCoin || + loadingDailyUsdVolumeByCrossed || + loadingDailyUsdVolumeByUser; - const error = errorCumulativeUsdVolume || errorDailyUsdVolume || errorDailyUsdVolumeByCoin || errorDailyUsdVolumeByCrossed || errorDailyUsdVolumeByUser; + const error = + errorCumulativeUsdVolume || + errorDailyUsdVolume || + errorDailyUsdVolumeByCoin || + errorDailyUsdVolumeByCrossed || + errorDailyUsdVolumeByUser; - type CumulativeVolumeData = { cumulative: number, time: string }; + type CumulativeVolumeData = { cumulative: number; time: string }; - const formatCumulativeVolumeByTime = (dataCumulativeUsdVolume: CumulativeVolumeData[]): { [key: string]: number } => { - const result: { [key: string]: number } = {}; - for (const data of dataCumulativeUsdVolume) { - result[data.time] = data.cumulative; - } - return result; - }; - - type FormattedDailyVolumeData = { time: string, daily_usd_volume: number }; - - const formatDailyVolumeByTime = (dataCumulativeUsdVolume: FormattedDailyVolumeData[]): { [key: string]: number } => { - const result: { [key: string]: number } = {}; - for (const data of dataCumulativeUsdVolume) { - result[data.time] = data.daily_usd_volume; - } - return result; - }; + const formatCumulativeVolumeByTime = ( + dataCumulativeUsdVolume: CumulativeVolumeData[] + ): { [key: string]: number } => { + const result: { [key: string]: number } = {}; + for (const data of dataCumulativeUsdVolume) { + result[data.time] = data.cumulative; + } + return result; + }; - type VolumeData = { coin: string, daily_usd_volume: number, time: string }; - type FormattedVolumeData = any[] //{ time: string, all: number, [coin: string]: number }; + type FormattedDailyVolumeData = { time: string; daily_usd_volume: number }; - const formatVolumeByCoins = ( - dataDailyUsdVolumeByCoin: VolumeData[], - formattedCumulativeUsdVolume: { [key: string]: number }, - formattedDailyVolumeByTime: { [key: string]: number } - ): FormattedVolumeData[] => { - const temp: { [key: string]: { all: number, [coin: string]: number } } = {}; - for (const data of dataDailyUsdVolumeByCoin) { - if (!temp[data.time]) { - temp[data.time] = { all: 0 }; - } - temp[data.time][data.coin] = data.daily_usd_volume; - temp[data.time].all += data.daily_usd_volume; - } + const formatDailyVolumeByTime = ( + dataCumulativeUsdVolume: FormattedDailyVolumeData[] + ): { [key: string]: number } => { + const result: { [key: string]: number } = {}; + for (const data of dataCumulativeUsdVolume) { + result[data.time] = data.daily_usd_volume; + } + return result; + }; - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj) - .sort(([, aVolume], [, bVolume]) => bVolume - aVolume); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); + type VolumeData = { coin: string; daily_usd_volume: number; time: string }; + type FormattedVolumeData = any[]; //{ time: string, all: number, [coin: string]: number }; - const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); - return { - ...Object.fromEntries(top10Entries), - Other: otherVolume - }; - }; + const formatVolumeByCoins = ( + dataDailyUsdVolumeByCoin: VolumeData[], + formattedCumulativeUsdVolume: { [key: string]: number }, + formattedDailyVolumeByTime: { [key: string]: number } + ): FormattedVolumeData[] => { + const temp: { [key: string]: { all: number; [coin: string]: number } } = {}; + for (const data of dataDailyUsdVolumeByCoin) { + if (!temp[data.time]) { + temp[data.time] = { all: 0 }; + } + temp[data.time][data.coin] = data.daily_usd_volume; + temp[data.time].all += data.daily_usd_volume; + } - const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); - return { - time: new Date(time), - ...top10Volumes, - cumulative: formattedCumulativeUsdVolume[time as any], - all: formattedDailyVolumeByTime[time as any], - unit: "$", - }; - }); + const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { + const sortedEntries = Object.entries(obj).sort( + ([, aVolume], [, bVolume]) => bVolume - aVolume + ); + const top10Entries = sortedEntries.slice(0, 10); + const otherEntries = sortedEntries.slice(10); - return result; + const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); + return { + ...Object.fromEntries(top10Entries), + Other: otherVolume, + }; }; + const result: any[] = Object.entries(temp).map(([time, volumes]) => { + const top10Volumes = sortAndSliceTop10(volumes); + return { + time: new Date(time), + ...top10Volumes, + cumulative: formattedCumulativeUsdVolume[time as any], + all: formattedDailyVolumeByTime[time as any], + unit: '$', + }; + }); - const extractUniqueCoins = (formattedVolumeData: FormattedVolumeData[]): string[] => { - const coinSet = new Set(); - for (const data of formattedVolumeData) { - Object.keys(data).forEach(coin => { - if (coin !== 'all' && - coin !== 'cumulative' && - coin !== 'time' && - coin !== 'other' && - coin !== 'unit' - ) { - coinSet.add(coin); - } - }); - } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); + return result; + }; + + const extractUniqueCoins = (formattedVolumeData: FormattedVolumeData[]): string[] => { + const coinSet = new Set(); + for (const data of formattedVolumeData) { + Object.keys(data).forEach((coin) => { + if ( + coin !== 'all' && + coin !== 'cumulative' && + coin !== 'time' && + coin !== 'other' && + coin !== 'unit' + ) { + coinSet.add(coin); } + }); + } + const coinsArray = Array.from(coinSet); + if (coinsArray.includes('Other')) { + const index = coinsArray.indexOf('Other'); + coinsArray.splice(index, 1); + coinsArray.push('Other'); + } - return coinsArray; - }; + return coinsArray; + }; - type VolumeCrossedData = { crossed: boolean, daily_usd_volume: number, time: string } + type VolumeCrossedData = { crossed: boolean; daily_usd_volume: number; time: string }; - const formatVolumeByCrossed = ( - dataDailyUsdVolumeByCrossed: VolumeCrossedData[], - formattedCumulativeUsdVolume: { [key: string]: number }, - formattedDailyVolumeByTime: { [key: string]: number } - ): any[] => { - // Create a temporary object to collect the data - const temp: { [key: string]: any } = {}; + const formatVolumeByCrossed = ( + dataDailyUsdVolumeByCrossed: VolumeCrossedData[], + formattedCumulativeUsdVolume: { [key: string]: number }, + formattedDailyVolumeByTime: { [key: string]: number } + ): any[] => { + // Create a temporary object to collect the data + const temp: { [key: string]: any } = {}; - for (const data of dataDailyUsdVolumeByCrossed) { - if (!temp[data.time]) { - temp[data.time] = { all: 0, maker: 0, taker: 0 }; - } - // Assigning daily_usd_volume to 'maker' if crossed is true, else assign to 'taker' - if (data.crossed) { - temp[data.time].taker = data.daily_usd_volume; - } else { - temp[data.time].maker = data.daily_usd_volume; - } - temp[data.time].all += data.daily_usd_volume; - } - // Convert the collected data into an array - const result: any[] = Object.entries(temp).map((item: any) => { - return { - time: new Date(item[0]), - maker: item[1].maker || 0, - taker: item[1].taker || 0, - cumulative: formattedCumulativeUsdVolume[item[0]], - all: formattedDailyVolumeByTime[item[0]], - unit: "$", - }; - }); - return result; - }; + for (const data of dataDailyUsdVolumeByCrossed) { + if (!temp[data.time]) { + temp[data.time] = { all: 0, maker: 0, taker: 0 }; + } + // Assigning daily_usd_volume to 'maker' if crossed is true, else assign to 'taker' + if (data.crossed) { + temp[data.time].taker = data.daily_usd_volume; + } else { + temp[data.time].maker = data.daily_usd_volume; + } + temp[data.time].all += data.daily_usd_volume; + } + // Convert the collected data into an array + const result: any[] = Object.entries(temp).map((item: any) => { + return { + time: new Date(item[0]), + maker: item[1].maker || 0, + taker: item[1].taker || 0, + cumulative: formattedCumulativeUsdVolume[item[0]], + all: formattedDailyVolumeByTime[item[0]], + unit: '$', + }; + }); + return result; + }; - const formatData = () => { - const formattedCumulativeVolumeByTime = formatCumulativeVolumeByTime(dataCumulativeUsdVolume) - const formattedDailyVolumeByTime = formatDailyVolumeByTime(dataDailyUsdVolume) - const formattedVolumeByCoins = formatVolumeByCoins(dataDailyUsdVolumeByCoin, formattedCumulativeVolumeByTime, formattedDailyVolumeByTime); - const formattedVolumeByCrossed = formatVolumeByCrossed(dataDailyUsdVolumeByCrossed, formattedCumulativeVolumeByTime, formattedDailyVolumeByTime); - setCoinKeys(extractUniqueCoins(formattedVolumeByCoins)) - setFormattedDataCoins(formattedVolumeByCoins); - setFormattedDataMarin(formattedVolumeByCrossed) - }; + const formatData = () => { + const formattedCumulativeVolumeByTime = formatCumulativeVolumeByTime(dataCumulativeUsdVolume); + const formattedDailyVolumeByTime = formatDailyVolumeByTime(dataDailyUsdVolume); + const formattedVolumeByCoins = formatVolumeByCoins( + dataDailyUsdVolumeByCoin, + formattedCumulativeVolumeByTime, + formattedDailyVolumeByTime + ); + const formattedVolumeByCrossed = formatVolumeByCrossed( + dataDailyUsdVolumeByCrossed, + formattedCumulativeVolumeByTime, + formattedDailyVolumeByTime + ); + setCoinKeys(extractUniqueCoins(formattedVolumeByCoins)); + setFormattedDataCoins(formattedVolumeByCoins); + setFormattedDataMarin(formattedVolumeByCrossed); + }; - const controls = { - toggles: [ - { - text: "Coins", - event: () => setDataMode('COINS'), - active: dataMode === 'COINS', - }, - { - text: "Maker / Taker", - event: () => setDataMode('MARGIN'), - active: dataMode === 'MARGIN', - } - ] - } + const controls = { + toggles: [ + { + text: 'Coins', + event: () => setDataMode('COINS'), + active: dataMode === 'COINS', + }, + { + text: 'Maker / Taker', + event: () => setDataMode('MARGIN'), + active: dataMode === 'MARGIN', + }, + ], + }; - useEffect(() => { - if (!loading) { - formatData(); - } - }, [loading]) + useEffect(() => { + if (!loading) { + formatData(); + } + }, [loading]); - return ( - + + - - - - - - - { - return Number(item.value) * -1; - }} - /> - - { - dataMode === 'COINS' && ( - <> - { - coinKeys.map(((coinName, i) => { - return ( - - ) - })) - } - - ) - } - { - dataMode === 'MARGIN' && ( - <> - - - - - ) - } - - - - - {dataMode === 'COINS' && ( - Top 10 Coins grouped daily and remaining coins grouped by Other - )} - - - ) -} \ No newline at end of file + + + + + { + return Number(item.value) * -1; + }} + /> + + {dataMode === 'COINS' && ( + <> + {coinKeys.map((coinName, i) => { + return ( + + ); + })} + + )} + {dataMode === 'MARGIN' && ( + <> + + + + )} + + + + + {dataMode === 'COINS' && ( + Top 10 Coins grouped daily and remaining coins grouped by Other + )} + + + ); +} diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index 874858d..cbdd389 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -1,353 +1,408 @@ import { - Bar, - Label, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ComposedChart, - Line + Bar, + Label, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, } from 'recharts'; -import { Box, Text, useMediaQuery } from "@chakra-ui/react" +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; import { - CHART_HEIGHT, - YAXIS_WIDTH, - BRIGHT_GREEN, - BRAND_GREEN_2, - BRAND_GREEN_3, -} from "../../../constants"; + CHART_HEIGHT, + YAXIS_WIDTH, + BRIGHT_GREEN, + BRAND_GREEN_2, + BRAND_GREEN_3, +} from '../../../constants'; import { - tooltipFormatter, - tooltipLabelFormatter, - yaxisFormatterNumber, - xAxisFormatter, -} from '../../../helpers' -import { getTokenHex } from "../../../constants/tokens"; + tooltipFormatter, + tooltipLabelFormatter, + yaxisFormatterNumber, + xAxisFormatter, +} from '../../../helpers'; +import { getTokenHex } from '../../../constants/tokens'; import { - cumulative_trades, - daily_trades, - daily_trades_by_coin, - daily_trades_by_crossed, - daily_trades_by_user, -} from "../../../constants/api" + cumulative_trades, + daily_trades, + daily_trades_by_coin, + daily_trades_by_crossed, + daily_trades_by_user, +} from '../../../constants/api'; const REQUESTS = [ - daily_trades, - daily_trades_by_coin, - daily_trades_by_crossed, - daily_trades_by_user, - cumulative_trades, + daily_trades, + daily_trades_by_coin, + daily_trades_by_crossed, + daily_trades_by_user, + cumulative_trades, ]; export default function VolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<"COINS" | "MARGIN" | "USER">('COINS'); - const [formattedDataCoins, setFormattedDataCoins] = useState([]) - const [formattedDataMarin, setFormattedDataMarin] = useState([]) - const [formattedDataUsers, setFormattedDataUsers] = useState([]) - const [maxAllValueUser, setMaxAllValueUser] = useState('auto'); - const [uniqueUsers, setUniqueUsers] = useState([]) - const [coinKeys, setCoinKeys] = useState([]) + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'USER'>('COINS'); + const [formattedDataCoins, setFormattedDataCoins] = useState([]); + const [formattedDataMarin, setFormattedDataMarin] = useState([]); + const [formattedDataUsers, setFormattedDataUsers] = useState([]); + const [maxAllValueUser, setMaxAllValueUser] = useState('auto'); + const [uniqueUsers, setUniqueUsers] = useState([]); + const [coinKeys, setCoinKeys] = useState([]); - const [dataDailyTrades, loadingDailyTrades, errorDailyTrades] = useRequest(REQUESTS[0], [], 'chart_data'); - const [dataDailyTradesByCoin, loadingDailyTradesByCoin, errorDailyTradesByCoin] = useRequest(REQUESTS[1], [], 'chart_data'); - const [dataDailyTradesByMargin, loadingDailyTradesByMargin, errorDailyTradesByMargin] = useRequest(REQUESTS[2], [], 'chart_data'); - const [dataDailyTradesByUser, loadingDailyTradesByUser, errorDailyTradesByUser] = useRequest(REQUESTS[3], [], 'chart_data'); - const [dataCumulativeTrades, loadingCumulativeTrades, errorCumulativeTrades] = useRequest(REQUESTS[4], [], 'chart_data'); + const [dataDailyTrades, loadingDailyTrades, errorDailyTrades] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + const [dataDailyTradesByCoin, loadingDailyTradesByCoin, errorDailyTradesByCoin] = useRequest( + REQUESTS[1], + [], + 'chart_data' + ); + const [dataDailyTradesByMargin, loadingDailyTradesByMargin, errorDailyTradesByMargin] = + useRequest(REQUESTS[2], [], 'chart_data'); + const [dataDailyTradesByUser, loadingDailyTradesByUser, errorDailyTradesByUser] = useRequest( + REQUESTS[3], + [], + 'chart_data' + ); + const [dataCumulativeTrades, loadingCumulativeTrades, errorCumulativeTrades] = useRequest( + REQUESTS[4], + [], + 'chart_data' + ); - const loading = loadingDailyTrades || loadingDailyTradesByCoin || loadingDailyTradesByMargin || loadingDailyTradesByUser || loadingCumulativeTrades; + const loading = + loadingDailyTrades || + loadingDailyTradesByCoin || + loadingDailyTradesByMargin || + loadingDailyTradesByUser || + loadingCumulativeTrades; - const error = errorDailyTrades || errorDailyTradesByCoin || errorDailyTradesByMargin || errorDailyTradesByUser || errorCumulativeTrades; - `` + const error = + errorDailyTrades || + errorDailyTradesByCoin || + errorDailyTradesByMargin || + errorDailyTradesByUser || + errorCumulativeTrades; + ``; - type CumulativeTradeData = { cumulative: number, time: string }; + type CumulativeTradeData = { cumulative: number; time: string }; - const formatTradesByTime = (dataCumulativeUsdVolume: CumulativeTradeData[]): { [key: string]: number } => { - const result: { [key: string]: number } = {}; - for (const data of dataCumulativeUsdVolume) { - result[data.time] = data.cumulative; - } - return result; - }; - - type DailyTradesData = { time: string, daily_trades: number }; + const formatTradesByTime = ( + dataCumulativeUsdVolume: CumulativeTradeData[] + ): { [key: string]: number } => { + const result: { [key: string]: number } = {}; + for (const data of dataCumulativeUsdVolume) { + result[data.time] = data.cumulative; + } + return result; + }; - const formatDailyTradesByTime = (dataDailyTrades: DailyTradesData[]): { [key: string]: number } => { - const result: { [key: string]: number } = {}; - for (const data of dataDailyTrades) { - result[data.time] = data.daily_trades; - } - return result; - }; + type DailyTradesData = { time: string; daily_trades: number }; - type CoinTradesData = { coin: string, daily_usd_volume: number, time: string }; - type FormattedCoinTradesData = any[] //{ time: string, all: number, [coin: string]: number }; + const formatDailyTradesByTime = ( + dataDailyTrades: DailyTradesData[] + ): { [key: string]: number } => { + const result: { [key: string]: number } = {}; + for (const data of dataDailyTrades) { + result[data.time] = data.daily_trades; + } + return result; + }; - const formatDailyTradesByCoins = ( - dataDailyTradesByCoin: { coin: string, daily_trades: number, time: string }[], - formattedCumulativeTradesByTime: { [key: string]: number }, - ): FormattedCoinTradesData[] => { - const temp: { [key: string]: { all: number, [coin: string]: number } } = {}; - for (const data of dataDailyTradesByCoin) { - if (!temp[data.time]) { - temp[data.time] = { all: 0 }; - } - temp[data.time][data.coin] = data.daily_trades; - temp[data.time].all += data.daily_trades; - } + type CoinTradesData = { coin: string; daily_usd_volume: number; time: string }; + type FormattedCoinTradesData = any[]; //{ time: string, all: number, [coin: string]: number }; - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj) - .sort(([, aVolume], [, bVolume]) => bVolume - aVolume); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); + const formatDailyTradesByCoins = ( + dataDailyTradesByCoin: { coin: string; daily_trades: number; time: string }[], + formattedCumulativeTradesByTime: { [key: string]: number } + ): FormattedCoinTradesData[] => { + const temp: { [key: string]: { all: number; [coin: string]: number } } = {}; + for (const data of dataDailyTradesByCoin) { + if (!temp[data.time]) { + temp[data.time] = { all: 0 }; + } + temp[data.time][data.coin] = data.daily_trades; + temp[data.time].all += data.daily_trades; + } - const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); - return { - ...Object.fromEntries(top10Entries), - Other: otherVolume - }; - }; + const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { + const sortedEntries = Object.entries(obj).sort( + ([, aVolume], [, bVolume]) => bVolume - aVolume + ); + const top10Entries = sortedEntries.slice(0, 10); + const otherEntries = sortedEntries.slice(10); - const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); - return { - time: new Date(time), - ...top10Volumes, - cumulative: formattedCumulativeTradesByTime[time as any], - unit: '', - }; - }); - return result; + const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); + return { + ...Object.fromEntries(top10Entries), + Other: otherVolume, + }; }; - const extractUniqueCoins = (formattedCoinTradesData: FormattedCoinTradesData[]): string[] => { - const coinSet = new Set(); - for (const data of formattedCoinTradesData) { - Object.keys(data).forEach(coin => { - if (coin !== 'all' && coin !== 'cumulative' && coin !== 'time' && coin !== 'unit') { - coinSet.add(coin); - } - }); - } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } - return coinsArray; - }; + const result: any[] = Object.entries(temp).map(([time, volumes]) => { + const top10Volumes = sortAndSliceTop10(volumes); + return { + time: new Date(time), + ...top10Volumes, + cumulative: formattedCumulativeTradesByTime[time as any], + unit: '', + }; + }); + return result; + }; - type MarginData = { crossed: boolean, daily_trades: number, time: string }; - type FormattedMarginData = { time: Date, maker: number, taker: number, all: number, cumulative: number }; + const extractUniqueCoins = (formattedCoinTradesData: FormattedCoinTradesData[]): string[] => { + const coinSet = new Set(); + for (const data of formattedCoinTradesData) { + Object.keys(data).forEach((coin) => { + if (coin !== 'all' && coin !== 'cumulative' && coin !== 'time' && coin !== 'unit') { + coinSet.add(coin); + } + }); + } + const coinsArray = Array.from(coinSet); + if (coinsArray.includes('Other')) { + const index = coinsArray.indexOf('Other'); + coinsArray.splice(index, 1); + coinsArray.push('Other'); + } + return coinsArray; + }; - const formatTradesByMargin = ( - dataDailyTradesByMargin: MarginData[], - formattedCumulativeTradesByTime: { [key: string]: number }, - ): FormattedMarginData[] => { - const groupedByTime: { [key: string]: FormattedMarginData } = {}; + type MarginData = { crossed: boolean; daily_trades: number; time: string }; + type FormattedMarginData = { + time: Date; + maker: number; + taker: number; + all: number; + cumulative: number; + }; - for (const data of dataDailyTradesByMargin) { - const dateKey = new Date(data.time).toISOString(); + const formatTradesByMargin = ( + dataDailyTradesByMargin: MarginData[], + formattedCumulativeTradesByTime: { [key: string]: number } + ): FormattedMarginData[] => { + const groupedByTime: { [key: string]: FormattedMarginData } = {}; - if (!groupedByTime[dateKey]) { - groupedByTime[dateKey] = { - time: new Date(data.time), - maker: 0, - taker: 0, - all: 0, - cumulative: 0, - } - } + for (const data of dataDailyTradesByMargin) { + const dateKey = new Date(data.time).toISOString(); - if (data.crossed) { - groupedByTime[dateKey].taker += data.daily_trades; - } else { - groupedByTime[dateKey].maker += data.daily_trades; - } - groupedByTime[dateKey].all += data.daily_trades; - groupedByTime[dateKey].cumulative = formattedCumulativeTradesByTime[data.time as any]; - } + if (!groupedByTime[dateKey]) { + groupedByTime[dateKey] = { + time: new Date(data.time), + maker: 0, + taker: 0, + all: 0, + cumulative: 0, + }; + } - return Object.values(groupedByTime); + if (data.crossed) { + groupedByTime[dateKey].taker += data.daily_trades; + } else { + groupedByTime[dateKey].maker += data.daily_trades; + } + groupedByTime[dateKey].all += data.daily_trades; + groupedByTime[dateKey].cumulative = formattedCumulativeTradesByTime[data.time as any]; } - const formatData = () => { - const formattedCumulativeTradesByTime = formatTradesByTime(dataCumulativeTrades) - const formattedTradesByCoins = formatDailyTradesByCoins(dataDailyTradesByCoin, formattedCumulativeTradesByTime); - const formattedTradesByMargin = formatTradesByMargin(dataDailyTradesByMargin, formattedCumulativeTradesByTime); - setMaxAllValueUser(maxAllValueUser); - setCoinKeys(extractUniqueCoins(formattedTradesByCoins)) - setFormattedDataCoins(formattedTradesByCoins); - setFormattedDataMarin(formattedTradesByMargin); - }; + return Object.values(groupedByTime); + }; - const controls = { - toggles: [ - { - text: "Coins", - event: () => setDataMode('COINS'), - active: dataMode === 'COINS', - }, - { - text: "Maker / Taker", - event: () => setDataMode('MARGIN'), - active: dataMode === 'MARGIN', - } - ] - } + const formatData = () => { + const formattedCumulativeTradesByTime = formatTradesByTime(dataCumulativeTrades); + const formattedTradesByCoins = formatDailyTradesByCoins( + dataDailyTradesByCoin, + formattedCumulativeTradesByTime + ); + const formattedTradesByMargin = formatTradesByMargin( + dataDailyTradesByMargin, + formattedCumulativeTradesByTime + ); + setMaxAllValueUser(maxAllValueUser); + setCoinKeys(extractUniqueCoins(formattedTradesByCoins)); + setFormattedDataCoins(formattedTradesByCoins); + setFormattedDataMarin(formattedTradesByMargin); + }; - useEffect(() => { - if (!loading && !error) { - formatData(); - } - }, [loading, dataMode]) + const controls = { + toggles: [ + { + text: 'Coins', + event: () => setDataMode('COINS'), + active: dataMode === 'COINS', + }, + { + text: 'Maker / Taker', + event: () => setDataMode('MARGIN'), + active: dataMode === 'MARGIN', + }, + ], + }; - return ( - - - - - { + if (!loading && !error) { + formatData(); + } + }, [loading, dataMode]); - /> - - - { - return Number(item.value) * -1; - }} - /> - {(dataMode === 'COINS' || dataMode === 'MARGIN') && } - { - dataMode === 'COINS' && ( - <> - { - coinKeys.map(((coinName, i) => { - return ( - - ) - })) - } - - ) - } - { - dataMode === 'MARGIN' && ( - <> - - - - ) - } - { - dataMode === 'USER' && ( - <> - { - uniqueUsers.map(((user, i) => { - return ( - - ) - })) - } - - ) - } - - - - - {dataMode === 'COINS' && ( - Top 10 Coins grouped daily and remaining coins grouped by Other - )} - - - ) -} \ No newline at end of file + return ( + + + + + + + + { + return Number(item.value) * -1; + }} + /> + {(dataMode === 'COINS' || dataMode === 'MARGIN') && ( + + )} + {dataMode === 'COINS' && ( + <> + {coinKeys.map((coinName, i) => { + return ( + + ); + })} + + )} + {dataMode === 'MARGIN' && ( + <> + + + + )} + {dataMode === 'USER' && ( + <> + {uniqueUsers.map((user, i) => { + return ( + + ); + })} + + )} + + + + + {dataMode === 'COINS' && ( + Top 10 Coins grouped daily and remaining coins grouped by Other + )} + + + ); +} diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index a415371..97a6860 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -1,6 +1,6 @@ -'use client' +'use client'; import React, { useState } from 'react'; -import moment from "moment"; +import moment from 'moment'; import { Container, Box, Text, Grid, Flex } from '@chakra-ui/react'; import * as S from './styles'; import TopStats from '../charts/top-stats'; @@ -12,84 +12,104 @@ import HLPProfitLossChart from '../charts/hlp-liquidator-profit'; import { DateRangeSelect } from '../charts/date-range'; import FundingRateChart from '../charts/funding-rate'; import CumulativeUsersChart from '../charts/cumulative-users'; -import CoinTradesByUsers from "../charts/unique-users-coin"; +import CoinTradesByUsers from '../charts/unique-users-coin'; import CumulativeInflowChart from '../charts/cumulative-inflow'; import CumulativeNotionalLiquidatedChart from '../charts/cumulative-notional-liquidated'; -import TableLargestUsers from "../tables/largest-users" -import TableUserDesposits from "../tables/user-deposits" -import TableLiquidatedNotional from "../tables/liquidated-notional-user" -import TableTradeCount from "../tables/user-trade-count" -import Liquidity from "../charts/liquidity" - +import TableLargestUsers from '../tables/largest-users'; +import TableUserDesposits from '../tables/user-deposits'; +import TableLiquidatedNotional from '../tables/liquidated-notional-user'; +import TableTradeCount from '../tables/user-trade-count'; +import Liquidity from '../charts/liquidity'; const Main = () => { - - return ( - + + + + Hyperliquid Stats + + + + + - - - - Hyperliquid Stats - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); }; export default Main; diff --git a/components/home/main/styles.ts b/components/home/main/styles.ts index fb9d11f..9b58e95 100644 --- a/components/home/main/styles.ts +++ b/components/home/main/styles.ts @@ -101,7 +101,7 @@ export const BgImg2 = styled(Box)` z-index: 0.5; animation: ${fade} 20s infinite; transition: visibility 20s ease-in-out; - + img { width: 100%; } @@ -129,4 +129,4 @@ export const ImgPlanet2 = styled(Box)` img { width: 100%; } -`; \ No newline at end of file +`; diff --git a/components/home/tables/largest-users.tsx b/components/home/tables/largest-users.tsx index 19e7321..2869078 100644 --- a/components/home/tables/largest-users.tsx +++ b/components/home/tables/largest-users.tsx @@ -1,116 +1,152 @@ -import React from "react"; -import { useTable, usePagination } from "react-table"; -import { Box, Button, ButtonGroup, Flex, Table, Tbody, Td, Th, Thead, Tr, Text, useMediaQuery } from "@chakra-ui/react"; +import React from 'react'; +import { useTable, usePagination } from 'react-table'; import { - largest_users_by_usd_volume, -} from "../../../constants/api" -import { - formatNumberWithOptions, -} from "../../../helpers/index" + Box, + Button, + ButtonGroup, + Flex, + Table, + Tbody, + Td, + Th, + Thead, + Tr, + Text, + useMediaQuery, +} from '@chakra-ui/react'; +import { largest_users_by_usd_volume } from '../../../constants/api'; +import { formatNumberWithOptions } from '../../../helpers/index'; import { formatAddress } from '../../../utils/formatting'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; -const REQUESTS = [ - largest_users_by_usd_volume -] +const REQUESTS = [largest_users_by_usd_volume]; export default function TableComponent() { + const [ + dataLargestUsersByUsdVolume, + loadingLargestUsersByUsdVolume, + errorLargestUsersByUsdVolume, + ] = useRequest(REQUESTS[0], [], 'table_data'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataLargestUsersByUsdVolume, loadingLargestUsersByUsdVolume, errorLargestUsersByUsdVolume] = useRequest(REQUESTS[0], [], 'table_data'); - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const columns = React.useMemo(() => [ - { - Header: "Address", - accessor: "name" as const, - Cell: ({ value }: { value: string }) => - - - {formatAddress(value, isMobile ? 6 : 6)} - - , - }, - { - Header: "Volume USD", - accessor: "value" as const, - Cell: ({ value }: { value: number }) => ${formatNumberWithOptions(value)}, - }, - ], []); + const columns = React.useMemo( + () => [ + { + Header: 'Address', + accessor: 'name' as const, + Cell: ({ value }: { value: string }) => ( + + + {formatAddress(value, isMobile ? 6 : 6)} + + + ), + }, + { + Header: 'Volume USD', + accessor: 'value' as const, + Cell: ({ value }: { value: number }) => ${formatNumberWithOptions(value)}, + }, + ], + [] + ); - const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - page, - canPreviousPage, - canNextPage, - pageOptions, - pageCount, - gotoPage, - nextPage, - previousPage, - state: { pageIndex, pageSize }, - } = useTable( - { - columns, - data: dataLargestUsersByUsdVolume ? dataLargestUsersByUsdVolume : [], - initialState: { pageIndex: 0, pageSize: 5 }, - }, - usePagination - ); + const { + getTableProps, + getTableBodyProps, + headerGroups, + prepareRow, + page, + canPreviousPage, + canNextPage, + pageOptions, + pageCount, + gotoPage, + nextPage, + previousPage, + state: { pageIndex, pageSize }, + } = useTable( + { + columns, + data: dataLargestUsersByUsdVolume ? dataLargestUsersByUsdVolume : [], + initialState: { pageIndex: 0, pageSize: 5 }, + }, + usePagination + ); - return ( - - - Largest Users By USD Volume - - - - {headerGroups.map((headerGroup, i) => ( - - {headerGroup.headers.map((column, j) => ( - - ))} - - ))} - - - {page.map((row: any, i: number) => { - prepareRow(row); - return ( - - {row.cells.map((cell: any, j: any) => { - return ; - })} - - ); - })} - -
{column.render('Header')}
{cell.render('Cell')}
- + + + Largest Users By USD Volume + + + + + {headerGroups.map((headerGroup, i) => ( + + {headerGroup.headers.map((column, j) => ( + + ))} + + ))} + + + {page.map((row: any, i: number) => { + prepareRow(row); + return ( + + {row.cells.map((cell: any, j: any) => { + return ( + + ); + })} + + ); + })} + +
+ {column.render('Header')} +
+ {cell.render('Cell')} +
+ + + + + + + - - - - - - - Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByUsdVolume.length} - - -
- ); -} \ No newline at end of file + {'Last'} + + +
+ + Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByUsdVolume.length} + + + + ); +} diff --git a/components/home/tables/liquidated-notional-user.tsx b/components/home/tables/liquidated-notional-user.tsx index 7edd694..689e114 100644 --- a/components/home/tables/liquidated-notional-user.tsx +++ b/components/home/tables/liquidated-notional-user.tsx @@ -1,116 +1,152 @@ -import React from "react"; -import { useTable, usePagination } from "react-table"; -import { Box, Button, ButtonGroup, Flex, Table, Tbody, Td, Th, Thead, Tr, Text, useMediaQuery } from "@chakra-ui/react"; +import React from 'react'; +import { useTable, usePagination } from 'react-table'; import { - largest_liquidated_notional_by_user, -} from "../../../constants/api" -import { - formatNumberWithOptions -} from "../../../helpers/index" + Box, + Button, + ButtonGroup, + Flex, + Table, + Tbody, + Td, + Th, + Thead, + Tr, + Text, + useMediaQuery, +} from '@chakra-ui/react'; +import { largest_liquidated_notional_by_user } from '../../../constants/api'; +import { formatNumberWithOptions } from '../../../helpers/index'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; import { formatAddress } from '../../../utils/formatting'; -const REQUESTS = [ - largest_liquidated_notional_by_user -] +const REQUESTS = [largest_liquidated_notional_by_user]; export default function TableComponent() { + const [ + dataLargestLiquidatedNotional, + loadingLargestLiquidatedNotional, + errorLargestLiquidatedNotional, + ] = useRequest(REQUESTS[0], [], 'table_data'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataLargestLiquidatedNotional, loadingLargestLiquidatedNotional, errorLargestLiquidatedNotional] = useRequest(REQUESTS[0], [], 'table_data'); - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const columns = React.useMemo(() => [ - { - Header: "Address", - accessor: "name" as const, - Cell: ({ value }: { value: string }) => - - - {formatAddress(value, isMobile ? 6 : 6)} - - , - }, - { - Header: "Liquidated Position USD", - accessor: "value" as const, - Cell: ({ value }: { value: number }) => ${formatNumberWithOptions(value)}, - }, - ], []); + const columns = React.useMemo( + () => [ + { + Header: 'Address', + accessor: 'name' as const, + Cell: ({ value }: { value: string }) => ( + + + {formatAddress(value, isMobile ? 6 : 6)} + + + ), + }, + { + Header: 'Liquidated Position USD', + accessor: 'value' as const, + Cell: ({ value }: { value: number }) => ${formatNumberWithOptions(value)}, + }, + ], + [] + ); - const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - page, - canPreviousPage, - canNextPage, - pageOptions, - pageCount, - gotoPage, - nextPage, - previousPage, - state: { pageIndex, pageSize }, - } = useTable( - { - columns, - data: dataLargestLiquidatedNotional ? dataLargestLiquidatedNotional : [], - initialState: { pageIndex: 0, pageSize: 5 }, - }, - usePagination - ); + const { + getTableProps, + getTableBodyProps, + headerGroups, + prepareRow, + page, + canPreviousPage, + canNextPage, + pageOptions, + pageCount, + gotoPage, + nextPage, + previousPage, + state: { pageIndex, pageSize }, + } = useTable( + { + columns, + data: dataLargestLiquidatedNotional ? dataLargestLiquidatedNotional : [], + initialState: { pageIndex: 0, pageSize: 5 }, + }, + usePagination + ); - return ( - - - Largest Liquidated Users By USD - - - - {headerGroups.map((headerGroup, i) => ( - - {headerGroup.headers.map((column, j) => ( - - ))} - - ))} - - - {page.map((row: any, i: number) => { - prepareRow(row); - return ( - - {row.cells.map((cell: any, j: any) => { - return ; - })} - - ); - })} - -
{column.render('Header')}
{cell.render('Cell')}
- + + + Largest Liquidated Users By USD + + + + + {headerGroups.map((headerGroup, i) => ( + + {headerGroup.headers.map((column, j) => ( + + ))} + + ))} + + + {page.map((row: any, i: number) => { + prepareRow(row); + return ( + + {row.cells.map((cell: any, j: any) => { + return ( + + ); + })} + + ); + })} + +
+ {column.render('Header')} +
+ {cell.render('Cell')} +
+ + + + + + + - - - - - - - Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestLiquidatedNotional.length} - - -
- ); -} \ No newline at end of file + {'Last'} + + +
+ + Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestLiquidatedNotional.length} + + + + ); +} diff --git a/components/home/tables/user-deposits.tsx b/components/home/tables/user-deposits.tsx index ad319ed..70abf18 100644 --- a/components/home/tables/user-deposits.tsx +++ b/components/home/tables/user-deposits.tsx @@ -1,118 +1,149 @@ -import React from "react"; -import { useTable, usePagination } from "react-table"; -import { Box, Button, ButtonGroup, Flex, Table, Tbody, Td, Th, Thead, Tr, Text, useMediaQuery } from "@chakra-ui/react"; +import React from 'react'; +import { useTable, usePagination } from 'react-table'; import { - largest_user_depositors, -} from "../../../constants/api" -import { - formatNumberWithOptions -} from "../../../helpers/index" + Box, + Button, + ButtonGroup, + Flex, + Table, + Tbody, + Td, + Th, + Thead, + Tr, + Text, + useMediaQuery, +} from '@chakra-ui/react'; +import { largest_user_depositors } from '../../../constants/api'; +import { formatNumberWithOptions } from '../../../helpers/index'; import { useRequest } from '@/hooks/useRequest'; import { formatAddress } from '../../../utils/formatting'; import ChartWrapper from '../../common/chartWrapper'; - -const REQUESTS = [ - largest_user_depositors -] +const REQUESTS = [largest_user_depositors]; export default function TableComponent() { + const [dataLargestUsersByDeposits, loadingLargestUsersByDeposits, errorLargestUsersByDeposits] = + useRequest(REQUESTS[0], [], 'table_data'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataLargestUsersByDeposits, loadingLargestUsersByDeposits, errorLargestUsersByDeposits] = useRequest(REQUESTS[0], [], 'table_data'); - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const columns = React.useMemo(() => [ - { - Header: "Address", - accessor: "name" as const, - Cell: ({ value }: { value: string }) => - - - {formatAddress(value, isMobile ? 6 : 6)} - - , - - }, - { - Header: "Deposit USD", - accessor: "value" as const, - Cell: ({ value }: { value: number }) => ${formatNumberWithOptions(value)}, - }, - ], []); + const columns = React.useMemo( + () => [ + { + Header: 'Address', + accessor: 'name' as const, + Cell: ({ value }: { value: string }) => ( + + + {formatAddress(value, isMobile ? 6 : 6)} + + + ), + }, + { + Header: 'Deposit USD', + accessor: 'value' as const, + Cell: ({ value }: { value: number }) => ${formatNumberWithOptions(value)}, + }, + ], + [] + ); - const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - page, - canPreviousPage, - canNextPage, - pageOptions, - pageCount, - gotoPage, - nextPage, - previousPage, - state: { pageIndex, pageSize }, - } = useTable( - { - columns, - data: dataLargestUsersByDeposits ? dataLargestUsersByDeposits : [], - initialState: { pageIndex: 0, pageSize: 5 }, - }, - usePagination - ); + const { + getTableProps, + getTableBodyProps, + headerGroups, + prepareRow, + page, + canPreviousPage, + canNextPage, + pageOptions, + pageCount, + gotoPage, + nextPage, + previousPage, + state: { pageIndex, pageSize }, + } = useTable( + { + columns, + data: dataLargestUsersByDeposits ? dataLargestUsersByDeposits : [], + initialState: { pageIndex: 0, pageSize: 5 }, + }, + usePagination + ); - return ( - - - Largest User Deposits By USD Value - - - - {headerGroups.map((headerGroup, i) => ( - - {headerGroup.headers.map((column, j) => ( - - ))} - - ))} - - - {page.map((row: any, i: number) => { - prepareRow(row); - return ( - - {row.cells.map((cell: any, j: any) => { - return ; - })} - - ); - })} - -
{column.render('Header')}
{cell.render('Cell')}
- + + + Largest User Deposits By USD Value + + + + + {headerGroups.map((headerGroup, i) => ( + + {headerGroup.headers.map((column, j) => ( + + ))} + + ))} + + + {page.map((row: any, i: number) => { + prepareRow(row); + return ( + + {row.cells.map((cell: any, j: any) => { + return ( + + ); + })} + + ); + })} + +
+ {column.render('Header')} +
+ {cell.render('Cell')} +
+ + + + + + + - - - - - - - Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByDeposits.length} - - -
- ); -} \ No newline at end of file + {'Last'} + + +
+ + Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByDeposits.length} + + + + ); +} diff --git a/components/home/tables/user-trade-count.tsx b/components/home/tables/user-trade-count.tsx index 9487056..64dfb3e 100644 --- a/components/home/tables/user-trade-count.tsx +++ b/components/home/tables/user-trade-count.tsx @@ -1,118 +1,152 @@ -import React from "react"; -import { useTable, usePagination } from "react-table"; -import { Box, Button, ButtonGroup, Flex, Table, Tbody, Td, Th, Thead, Tr, Text, useMediaQuery } from "@chakra-ui/react"; +import React from 'react'; +import { useTable, usePagination } from 'react-table'; import { - largest_user_trade_count, -} from "../../../constants/api" -import { - formatNumberWithOptions -} from "../../../helpers/index" + Box, + Button, + ButtonGroup, + Flex, + Table, + Tbody, + Td, + Th, + Thead, + Tr, + Text, + useMediaQuery, +} from '@chakra-ui/react'; +import { largest_user_trade_count } from '../../../constants/api'; +import { formatNumberWithOptions } from '../../../helpers/index'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; import { formatAddress } from '../../../utils/formatting'; - -const REQUESTS = [ - largest_user_trade_count -] +const REQUESTS = [largest_user_trade_count]; export default function TableComponent() { + const [ + dataLargestUsersByTradeCount, + loadingLargestUsersByTradeCount, + errorLargestUsersByTradeCount, + ] = useRequest(REQUESTS[0], [], 'table_data'); + const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataLargestUsersByTradeCount, loadingLargestUsersByTradeCount, errorLargestUsersByTradeCount] = useRequest(REQUESTS[0], [], 'table_data'); - const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const columns = React.useMemo(() => [ - { - Header: "Address", - accessor: "name" as const, - Cell: ({ value }: { value: string }) => - - - {formatAddress(value, isMobile ? 6 : 6)} - - , - - }, - { - Header: "Number of Trades", - accessor: "value" as const, - Cell: ({ value }: { value: number }) => {formatNumberWithOptions(value)}, - }, - ], []); + const columns = React.useMemo( + () => [ + { + Header: 'Address', + accessor: 'name' as const, + Cell: ({ value }: { value: string }) => ( + + + {formatAddress(value, isMobile ? 6 : 6)} + + + ), + }, + { + Header: 'Number of Trades', + accessor: 'value' as const, + Cell: ({ value }: { value: number }) => {formatNumberWithOptions(value)}, + }, + ], + [] + ); - const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - page, - canPreviousPage, - canNextPage, - pageOptions, - pageCount, - gotoPage, - nextPage, - previousPage, - state: { pageIndex, pageSize }, - } = useTable( - { - columns, - data: dataLargestUsersByTradeCount ? dataLargestUsersByTradeCount : [], - initialState: { pageIndex: 0, pageSize: 5 }, - }, - usePagination - ); + const { + getTableProps, + getTableBodyProps, + headerGroups, + prepareRow, + page, + canPreviousPage, + canNextPage, + pageOptions, + pageCount, + gotoPage, + nextPage, + previousPage, + state: { pageIndex, pageSize }, + } = useTable( + { + columns, + data: dataLargestUsersByTradeCount ? dataLargestUsersByTradeCount : [], + initialState: { pageIndex: 0, pageSize: 5 }, + }, + usePagination + ); - return ( - - - Largest Trade Count by Users - - - - {headerGroups.map((headerGroup, i) => ( - - {headerGroup.headers.map((column, j) => ( - - ))} - - ))} - - - {page.map((row: any, i: number) => { - prepareRow(row); - return ( - - {row.cells.map((cell: any, j: any) => { - return ; - })} - - ); - })} - -
{column.render('Header')}
{cell.render('Cell')}
- + + + Largest Trade Count by Users + + + + + {headerGroups.map((headerGroup, i) => ( + + {headerGroup.headers.map((column, j) => ( + + ))} + + ))} + + + {page.map((row: any, i: number) => { + prepareRow(row); + return ( + + {row.cells.map((cell: any, j: any) => { + return ( + + ); + })} + + ); + })} + +
+ {column.render('Header')} +
+ {cell.render('Cell')} +
+ + + + + + + - - - - - - - Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByTradeCount.length} - - -
- ); -} \ No newline at end of file + {'Last'} + + + + + Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByTradeCount.length} + + + + ); +} diff --git a/constants/api.ts b/constants/api.ts index 1a35682..8922ad0 100644 --- a/constants/api.ts +++ b/constants/api.ts @@ -1,48 +1,51 @@ -export const total_users = "/hyperliquid/total_users"; // Retrieves the total number of users. -export const total_usd_volume = "/hyperliquid/total_usd_volume"; // Retrieves the total USD trading volume. -export const total_deposits = "/hyperliquid/total_deposits"; // Retrieves the total amount of deposits. -export const total_withdrawals = "/hyperliquid/total_withdrawals"; // Retrieves the total amount of withdrawals. -export const total_notional_liquidated = "/hyperliquid/total_notional_liquidated"; // Retrieves the total notional value liquidated. -export const cumulative_usd_volume = "/hyperliquid/cumulative_usd_volume"; // Retrieves the cumulative USD trading volume over time. -export const daily_usd_volume = "/hyperliquid/daily_usd_volume"; // Retrieves the daily USD trading volume over time. -export const daily_usd_volume_by_coin = "/hyperliquid/daily_usd_volume_by_coin"; // Retrieves the daily USD trading volume by coin over time. -export const daily_usd_volume_by_crossed = "/hyperliquid/daily_usd_volume_by_crossed"; // Retrieves the daily USD trading volume by crossed over time. -export const daily_usd_volume_by_user = "/hyperliquid/daily_usd_volume_by_user"; // Retrieves the daily USD trading volume by top 10 user and the rest are summed and marked as other. - -export const cumulative_trades = "/hyperliquid/cumulative_trades"; // Retrieves the cumulative number of trades over time. The line chart of Cumulative total trades chart. - -export const daily_trades = "/hyperliquid/daily_trades"; // Retrieves the daily number of trades. -export const daily_trades_by_coin = "/hyperliquid/daily_trades_by_coin"; // Retrieves the daily number of trades by coin. -export const daily_trades_by_crossed = "/hyperliquid/daily_trades_by_crossed"; // Retrieves the daily number of trades by crossed. -export const daily_trades_by_user = "/hyperliquid/daily_trades_by_user"; // Retrieves the daily number of trades by top 10 user and the rest are summed and marked as other. -export const user_pnl = "/hyperliquid/user_pnl"; // Retrieves the profit and loss (PnL) for all users daily. -export const cumulative_user_pnl = "/hyperliquid/cumulative_user_pnl"; // Retrieves the cumulative PnL for all users over time. -export const hlp_liquidator_pnl = "/hyperliquid/hlp_liquidator_pnl"; // Retrieves the PnL for liquidators daily. -export const hlp_liquidator_pnl_false = "/hyperliquid/hlp_liquidator_pnl?is_hlp=false"; // Retrieves the PnL for liquidators daily. - -export const cumulative_hlp_liquidator_pnl = "/hyperliquid/cumulative_hlp_liquidator_pnl"; // Retrieves the cumulative PnL for liquidators over time. - -export const cumulative_hlp_liquidator_pnl_false = "/hyperliquid/cumulative_hlp_liquidator_pnl?is_hlp=false"; // Retrieves the cumulative PnL for liquidators over time. - -export const cumulative_liquidated_notional = "/hyperliquid/cumulative_liquidated_notional"; // Retrieves the cumulative liquidated notional value over time. -export const daily_unique_users = "/hyperliquid/daily_unique_users"; // Retrieves the daily number of unique users. -export const cumulative_users = "/hyperliquid/cumulative_users"; // Retrieves the cumulative number of users over time - -export const daily_unique_users_by_coin = "/hyperliquid/daily_unique_users_by_coin"; - -export const cumulative_inflow = "/hyperliquid/cumulative_inflow"; // Retrieves the cumulative inflow of funds over time. -export const open_interest = "/hyperliquid/open_interest"; // Retrieves the open interest data. -export const funding_rate = "/hyperliquid/funding_rate"; // Retrieves the funding rate data. -export const cumulative_new_users = "/hyperliquid/cumulative_new_users"; // Retrieves the cumulative number of unique users over time. -export const daily_inflow = "/hyperliquid/daily_inflow"; // Retrieves the daily inflow of funds. -export const liquidity_per_symbol = "/hyperliquid/liquidity_per_symbol"; // Retrieves the liquidity data per symbol. -export const largest_users_by_usd_volume = "/hyperliquid/largest_users_by_usd_volume"; // Retrieves the largest users by USD trading volume. -export const largest_user_depositors = "/hyperliquid/largest_user_depositors"; // Retrieves the largest user depositors. -export const largest_liquidated_notional_by_user = "/hyperliquid/largest_liquidated_notional_by_user"; // Retrieves the largest liquidated notional by user. -export const largest_user_trade_count = "/hyperliquid/largest_user_trade_count"; // Retrieves the users with the highest trade counts. - -export const daily_notional_liquidated_total = "/hyperliquid/daily_notional_liquidated_total"; -export const daily_notional_liquidated_by_leverage_type = "/hyperliquid/daily_notional_liquidated_by_leverage_type"; - -export const liquidity_by_coin = "/hyperliquid/liquidity_by_coin"; -export const daily_notional_liquidated_by_coin = "/hyperliquid/daily_notional_liquidated_by_coin"; \ No newline at end of file +export const total_users = '/hyperliquid/total_users'; // Retrieves the total number of users. +export const total_usd_volume = '/hyperliquid/total_usd_volume'; // Retrieves the total USD trading volume. +export const total_deposits = '/hyperliquid/total_deposits'; // Retrieves the total amount of deposits. +export const total_withdrawals = '/hyperliquid/total_withdrawals'; // Retrieves the total amount of withdrawals. +export const total_notional_liquidated = '/hyperliquid/total_notional_liquidated'; // Retrieves the total notional value liquidated. +export const cumulative_usd_volume = '/hyperliquid/cumulative_usd_volume'; // Retrieves the cumulative USD trading volume over time. +export const daily_usd_volume = '/hyperliquid/daily_usd_volume'; // Retrieves the daily USD trading volume over time. +export const daily_usd_volume_by_coin = '/hyperliquid/daily_usd_volume_by_coin'; // Retrieves the daily USD trading volume by coin over time. +export const daily_usd_volume_by_crossed = '/hyperliquid/daily_usd_volume_by_crossed'; // Retrieves the daily USD trading volume by crossed over time. +export const daily_usd_volume_by_user = '/hyperliquid/daily_usd_volume_by_user'; // Retrieves the daily USD trading volume by top 10 user and the rest are summed and marked as other. + +export const cumulative_trades = '/hyperliquid/cumulative_trades'; // Retrieves the cumulative number of trades over time. The line chart of Cumulative total trades chart. + +export const daily_trades = '/hyperliquid/daily_trades'; // Retrieves the daily number of trades. +export const daily_trades_by_coin = '/hyperliquid/daily_trades_by_coin'; // Retrieves the daily number of trades by coin. +export const daily_trades_by_crossed = '/hyperliquid/daily_trades_by_crossed'; // Retrieves the daily number of trades by crossed. +export const daily_trades_by_user = '/hyperliquid/daily_trades_by_user'; // Retrieves the daily number of trades by top 10 user and the rest are summed and marked as other. +export const user_pnl = '/hyperliquid/user_pnl'; // Retrieves the profit and loss (PnL) for all users daily. +export const cumulative_user_pnl = '/hyperliquid/cumulative_user_pnl'; // Retrieves the cumulative PnL for all users over time. +export const hlp_liquidator_pnl = '/hyperliquid/hlp_liquidator_pnl'; // Retrieves the PnL for liquidators daily. +export const hlp_liquidator_pnl_false = '/hyperliquid/hlp_liquidator_pnl?is_hlp=false'; // Retrieves the PnL for liquidators daily. + +export const cumulative_hlp_liquidator_pnl = '/hyperliquid/cumulative_hlp_liquidator_pnl'; // Retrieves the cumulative PnL for liquidators over time. + +export const cumulative_hlp_liquidator_pnl_false = + '/hyperliquid/cumulative_hlp_liquidator_pnl?is_hlp=false'; // Retrieves the cumulative PnL for liquidators over time. + +export const cumulative_liquidated_notional = '/hyperliquid/cumulative_liquidated_notional'; // Retrieves the cumulative liquidated notional value over time. +export const daily_unique_users = '/hyperliquid/daily_unique_users'; // Retrieves the daily number of unique users. +export const cumulative_users = '/hyperliquid/cumulative_users'; // Retrieves the cumulative number of users over time + +export const daily_unique_users_by_coin = '/hyperliquid/daily_unique_users_by_coin'; + +export const cumulative_inflow = '/hyperliquid/cumulative_inflow'; // Retrieves the cumulative inflow of funds over time. +export const open_interest = '/hyperliquid/open_interest'; // Retrieves the open interest data. +export const funding_rate = '/hyperliquid/funding_rate'; // Retrieves the funding rate data. +export const cumulative_new_users = '/hyperliquid/cumulative_new_users'; // Retrieves the cumulative number of unique users over time. +export const daily_inflow = '/hyperliquid/daily_inflow'; // Retrieves the daily inflow of funds. +export const liquidity_per_symbol = '/hyperliquid/liquidity_per_symbol'; // Retrieves the liquidity data per symbol. +export const largest_users_by_usd_volume = '/hyperliquid/largest_users_by_usd_volume'; // Retrieves the largest users by USD trading volume. +export const largest_user_depositors = '/hyperliquid/largest_user_depositors'; // Retrieves the largest user depositors. +export const largest_liquidated_notional_by_user = + '/hyperliquid/largest_liquidated_notional_by_user'; // Retrieves the largest liquidated notional by user. +export const largest_user_trade_count = '/hyperliquid/largest_user_trade_count'; // Retrieves the users with the highest trade counts. + +export const daily_notional_liquidated_total = '/hyperliquid/daily_notional_liquidated_total'; +export const daily_notional_liquidated_by_leverage_type = + '/hyperliquid/daily_notional_liquidated_by_leverage_type'; + +export const liquidity_by_coin = '/hyperliquid/liquidity_by_coin'; +export const daily_notional_liquidated_by_coin = '/hyperliquid/daily_notional_liquidated_by_coin'; diff --git a/constants/index.ts b/constants/index.ts index 5631e4f..78cb82b 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -1,10 +1,10 @@ -export const CHART_HEIGHT = 400 -export const YAXIS_WIDTH = 60 +export const CHART_HEIGHT = 400; +export const YAXIS_WIDTH = 60; -export const GREEN = '#62B143' -export const RED = '#DC0428' +export const GREEN = '#62B143'; +export const RED = '#DC0428'; export const BRIGHT_GREEN = '#97ffe4'; export const BRAND_GREEN_2 = '#4E8174'; export const BRAND_GREEN_3 = '#72BDAB'; -export const excluded_percentage_tooltip = ['daily_unique_users']; \ No newline at end of file +export const excluded_percentage_tooltip = ['daily_unique_users']; diff --git a/constants/tokens.ts b/constants/tokens.ts index b37b8dd..d42bcc4 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -1,37 +1,37 @@ const TOKEN_COLORS: any = { - "BNB": "#ebc509", - "BTC": "#F2A900", - "DOGE": "#cb9800", - "ETH": "#8A94B1", - "INJ": "#0386FA", - "KPEPE": "#509844", - "MATIC": "#8C35D5", - "Other": "#BBBAC6", - "SOL": "#C867F0", - "AVAX": "#e74242", - "LTC": "#CCCCCC", - "ARB": "#FCA100", - "LINK": "#81D2FD", - "APE": "#4087BE", - "ATOM": "#FCA100", - "CFX": "#F3654E", - "CRV": "#850087", - "DYDX": "#BE586C", - "FTM": "#568EC0", - "GMX": "#59C782", - "LDO": "#DB6ED7", - "OP": "#7F0182", - "RNDR": "#FFA300", - "SNX": "#498548", - "STX": "#578374", - "SUI": "#6A807A" -} + BNB: '#ebc509', + BTC: '#F2A900', + DOGE: '#cb9800', + ETH: '#8A94B1', + INJ: '#0386FA', + KPEPE: '#509844', + MATIC: '#8C35D5', + Other: '#BBBAC6', + SOL: '#C867F0', + AVAX: '#e74242', + LTC: '#CCCCCC', + ARB: '#FCA100', + LINK: '#81D2FD', + APE: '#4087BE', + ATOM: '#FCA100', + CFX: '#F3654E', + CRV: '#850087', + DYDX: '#BE586C', + FTM: '#568EC0', + GMX: '#59C782', + LDO: '#DB6ED7', + OP: '#7F0182', + RNDR: '#FFA300', + SNX: '#498548', + STX: '#578374', + SUI: '#6A807A', +}; export const getTokenHex = (token: string) => { - const symbol = token.toUpperCase() - if (TOKEN_COLORS[symbol]) { - return TOKEN_COLORS[symbol]; - } else { - return "pink" - } -} \ No newline at end of file + const symbol = token.toUpperCase(); + if (TOKEN_COLORS[symbol]) { + return TOKEN_COLORS[symbol]; + } else { + return 'pink'; + } +}; diff --git a/contexts/data.tsx b/contexts/data.tsx index 2fbad6b..5f2b30d 100644 --- a/contexts/data.tsx +++ b/contexts/data.tsx @@ -1,46 +1,41 @@ -import React, { useState } from "react"; -import strftime from "strftime"; +import React, { useState } from 'react'; +import strftime from 'strftime'; type Dates = { - to: string | undefined; - from: string | undefined; -} + to: string | undefined; + from: string | undefined; +}; export interface State { - dates: Dates; - setDates: (dates: Dates) => void; + dates: Dates; + setDates: (dates: Dates) => void; } const DATE_NOW = new Date(); const DATE_TO = strftime('%Y-%m-%d', DATE_NOW); export const DataContext = React.createContext({ + dates: { + from: undefined, + to: undefined, + }, + setDates: (dates: Dates) => {}, +}); + +export const DataContextProvider = (props: any) => { + const setDates = (dates: Dates) => { + setState({ ...state, dates }); + }; + + const initState: State = { dates: { - from: undefined, - to: undefined, + from: '2023-05-10', + to: DATE_TO, }, - setDates: (dates: Dates) => { }, -}) + setDates: setDates, + }; -export const DataContextProvider = (props: any) => { + const [state, setState] = useState(initState); - const setDates = (dates: Dates) => { - setState({ ...state, dates }); - } - - const initState: State = { - dates: { - from: '2023-05-10', - to: DATE_TO, - }, - setDates: setDates, - }; - - const [state, setState] = useState(initState); - - return ( - - {props.children} - - ) -} \ No newline at end of file + return {props.children}; +}; diff --git a/helpers/index.ts b/helpers/index.ts index faf294e..8ec426a 100644 --- a/helpers/index.ts +++ b/helpers/index.ts @@ -1,16 +1,40 @@ -import strftime from 'strftime' - -import { excluded_percentage_tooltip } from "../constants" - -const numberFmt0: Intl.NumberFormat = new Intl.NumberFormat('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 }); -const numberFmt1: Intl.NumberFormat = new Intl.NumberFormat('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 1 }); -const numberFmt2: Intl.NumberFormat = new Intl.NumberFormat('en-US', { minimumFractionDigits: 3, maximumFractionDigits: 3 }); -const currencyFmt0: Intl.NumberFormat = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }); -const currencyFmt1: Intl.NumberFormat = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 1, maximumFractionDigits: 1 }); -const currencyFmt2: Intl.NumberFormat = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 }); +import strftime from 'strftime'; + +import { excluded_percentage_tooltip } from '../constants'; + +const numberFmt0: Intl.NumberFormat = new Intl.NumberFormat('en-US', { + minimumFractionDigits: 0, + maximumFractionDigits: 0, +}); +const numberFmt1: Intl.NumberFormat = new Intl.NumberFormat('en-US', { + minimumFractionDigits: 0, + maximumFractionDigits: 1, +}); +const numberFmt2: Intl.NumberFormat = new Intl.NumberFormat('en-US', { + minimumFractionDigits: 3, + maximumFractionDigits: 3, +}); +const currencyFmt0: Intl.NumberFormat = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 0, + maximumFractionDigits: 0, +}); +const currencyFmt1: Intl.NumberFormat = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 1, + maximumFractionDigits: 1, +}); +const currencyFmt2: Intl.NumberFormat = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 2, + maximumFractionDigits: 2, +}); function getNumberFormatBasedOnValue(value: number): Intl.NumberFormat { - const absValue = Math.abs(value) + const absValue = Math.abs(value); if (absValue < 10) { return numberFmt2; } else if (absValue < 1000) { @@ -21,7 +45,7 @@ function getNumberFormatBasedOnValue(value: number): Intl.NumberFormat { } function getCurrencyFormatBasedOnValue(value: number): Intl.NumberFormat { - const absValue = Math.abs(value) + const absValue = Math.abs(value); if (absValue < 10) { return currencyFmt2; } else if (absValue < 1000) { @@ -37,22 +61,24 @@ interface FormatNumberOpts { } export const formatNumberWithOptions = (value: number, opts: FormatNumberOpts = {}): string => { - const currency = !!opts.currency - const compact = !!opts.compact + const currency = !!opts.currency; + const compact = !!opts.compact; if (currency && !compact) { return getCurrencyFormatBasedOnValue(value).format(value); } - const display = compact ? formatNumberToCompactForm(value) : getNumberFormatBasedOnValue(value).format(value); + const display = compact + ? formatNumberToCompactForm(value) + : getNumberFormatBasedOnValue(value).format(value); if (currency) { return `$${display}`; } return display; -} +}; export const formatNumberToCompactForm = (value: number): string => { - const abs = Math.abs(value) + const abs = Math.abs(value); if (abs >= 1e9) { return `${(value / 1e9).toFixed(abs < 1e10 ? 2 : 1)}B`; } @@ -63,116 +89,123 @@ export const formatNumberToCompactForm = (value: number): string => { return `${(value / 1e3).toFixed(abs < 1e4 ? 2 : 1)}K`; } return `${value.toFixed(1)}`; -} - +}; export const tooltipLabelFormatterPercent = (label: any, args: any): any => { - const hide = args && args[0] && args[0].payload && args[0].payload.unit && (args[0].payload.unit === '%' || args[0].payload.unit === 'single') ; + const hide = + args && + args[0] && + args[0].payload && + args[0].payload.unit && + (args[0].payload.unit === '%' || args[0].payload.unit === 'single'); if (hide) return ''; if (!label) return; if (label.constructor !== Date) { - label = new Date(label * 1000) + label = new Date(label * 1000); } - const item = args && args[0] && args[0].payload && args[0] - const dateFmtString = 'Total %d-%m-%y : ' - const date = strftime(dateFmtString, label) - const all = item && (item.payload.all) + const item = args && args[0] && args[0].payload && args[0]; + const dateFmtString = 'Total %d-%m-%y : '; + const date = strftime(dateFmtString, label); + const all = item && item.payload.all; if (all) { - return `${date} ${formatNumberWithOptions(all, {compact: true})}%` + return `${date} ${formatNumberWithOptions(all, { compact: true })}%`; } - return date -} + return date; +}; export const tooltipLabelFormatter = (label: any, args: any): any => { - const hide = args && args[0] && args[0].payload && args[0].payload.unit && (args[0].payload.unit === '%' || args[0].payload.unit === 'single') ; + const hide = + args && + args[0] && + args[0].payload && + args[0].payload.unit && + (args[0].payload.unit === '%' || args[0].payload.unit === 'single'); if (hide) return ''; if (!label) return; if (label.constructor !== Date) { - label = new Date(label * 1000) + label = new Date(label * 1000); } - const item = args && args[0] && args[0].payload && args[0] - const dateFmtString = 'Total %d-%m-%y :' - const date = strftime(dateFmtString, label) - const all = item && (item.payload.all) + const item = args && args[0] && args[0].payload && args[0]; + const dateFmtString = 'Total %d-%m-%y :'; + const date = strftime(dateFmtString, label); + const all = item && item.payload.all; if (all) { - if (item && item.unit === '$' || item.payload && item.payload.unit === '$') { - return `${date} ${formatNumberWithOptions(all, {currency: true, compact: true})}` + if ((item && item.unit === '$') || (item.payload && item.payload.unit === '$')) { + return `${date} ${formatNumberWithOptions(all, { currency: true, compact: true })}`; } - return `${date} ${formatNumberWithOptions(all, {compact: true})}` - + return `${date} ${formatNumberWithOptions(all, { compact: true })}`; } - return date -} + return date; +}; export const xAxisFormatter = (label: any, args: any): any => { if (!label) return; if (label.constructor !== Date) { - label = new Date(label * 1000) + label = new Date(label * 1000); } - const item = args && args[0] && args[0].payload && args[0] - const dateFmtString = '%d-%m' - const date = strftime(dateFmtString, label) - const all = item && (item.payload.all) + const item = args && args[0] && args[0].payload && args[0]; + const dateFmtString = '%d-%m'; + const date = strftime(dateFmtString, label); + const all = item && item.payload.all; if (all) { - if (item && item.unit === '%' || item.payload && item.payload.unit === '%') { - return `${date} ${formatNumberWithOptions(all, {compact: true})}%` + if ((item && item.unit === '%') || (item.payload && item.payload.unit === '%')) { + return `${date} ${formatNumberWithOptions(all, { compact: true })}%`; } - if (item && item.unit === '$' || item.payload && item.payload.unit === '$') { - return `${date} ${formatNumberWithOptions(all, {currency: true, compact: true})}` + if ((item && item.unit === '$') || (item.payload && item.payload.unit === '$')) { + return `${date} ${formatNumberWithOptions(all, { currency: true, compact: true })}`; } - return `${date} ${formatNumberWithOptions(all, {compact: true})}` + return `${date} ${formatNumberWithOptions(all, { compact: true })}`; } - return date -} + return date; +}; export const yaxisFormatterPercent = (value: number): string => { return value.toFixed(0) + '%'; -} - +}; export const formatterPercent = (value: number): string => { return value.toFixed(2) + '%'; -} +}; export const yaxisFormatterNumber = (value: number): string => { return formatNumberToCompactForm(value); -} +}; export const yaxisFormatter = (value: number): string => { - return formatNumberWithOptions(value, { currency: true, compact: true }); -} +}; export const tooltipFormatterNumber = (value: number | string): string => { return formatNumberWithOptions(Number(value), { compact: true }); -} +}; export const tooltipFormatterCurrency = (value: number | string): string => { return formatNumberWithOptions(Number(value), { currency: true, compact: true }); -} +}; export const tooltipFormatterPercent = (value: number): string => { return value.toFixed(2) + '%'; -} +}; export const tooltipFormatter = (value: any, name: any, item: any) => { if (excluded_percentage_tooltip.indexOf(item.dataKey) > -1) { return `${value}`; } - if (item && item.unit === '%' || item.payload && item.payload.unit === '%') { + if ((item && item.unit === '%') || (item.payload && item.payload.unit === '%')) { return `${value.toFixed(2)}%`; } if (item && !item.unit) { return formatNumberWithOptions(item.value, { currency: false }); } - return formatNumberWithOptions(value, { currency: true }) -} - + return formatNumberWithOptions(value, { currency: true }); +}; -export const tooltipLabelFormatterUnits = (label: number | Date, args?: any[]): string | undefined => { - +export const tooltipLabelFormatterUnits = ( + label: number | Date, + args?: any[] +): string | undefined => { if (!label) { return label as any | undefined; } @@ -190,10 +223,10 @@ export const tooltipLabelFormatterUnits = (label: number | Date, args?: any[]): return date; } - const all = item && (item.payload.all); + const all = item && item.payload.all; if (label.constructor !== Date) { return all ? `${label}, total: ${all}` : label.toString(); } return all ? `${date}, total: ${all}` : date; -} \ No newline at end of file +}; diff --git a/hooks/useRequest.ts b/hooks/useRequest.ts index 656ff7a..7ccc795 100644 --- a/hooks/useRequest.ts +++ b/hooks/useRequest.ts @@ -1,19 +1,23 @@ -import { useState, useEffect, useContext } from "react"; -import { DataContext } from "../contexts/data"; +import { useState, useEffect, useContext } from 'react'; +import { DataContext } from '../contexts/data'; -const fetcher = (url: string) => fetch(url).then(res => res.json()) +const fetcher = (url: string) => fetch(url).then((res) => res.json()); export function useRequest(url: string, defaultValue: any, key?: string, dontRefetch?: boolean) { const [loading, setLoading] = useState(true); const [error, setError] = useState(); const [data, setData] = useState(defaultValue); const dataContext = useContext(DataContext); - const urlHasParams = url.indexOf("?") !== -1; + const urlHasParams = url.indexOf('?') !== -1; let params = ''; if (dataContext.dates.from) { - if (!urlHasParams) { params += '?' } - if (urlHasParams) { params += '&' } + if (!urlHasParams) { + params += '?'; + } + if (urlHasParams) { + params += '&'; + } params += `start_date=${dataContext.dates.from}`; } if (dataContext.dates.to) { @@ -22,28 +26,28 @@ export function useRequest(url: string, defaultValue: any, key?: string, dontRef const init = async () => { try { - setLoading(true) - const data = await fetcher(`${process.env.NEXT_PUBLIC_API_URL}${url}${params}`); - if (key && data[key]) { - setData(data[key]) - } else { - setData(data) - } - } catch (error) { - console.error(error) - setError(error) + setLoading(true); + const data = await fetcher(`${process.env.NEXT_PUBLIC_API_URL}${url}${params}`); + if (key && data[key]) { + setData(data[key]); + } else { + setData(data); } - setLoading(false) - } + } catch (error) { + console.error(error); + setError(error); + } + setLoading(false); + }; useEffect(() => { init(); - }, [url]) + }, [url]); useEffect(() => { if (dontRefetch) return; init(); - }, [dataContext.dates.from, dataContext.dates.to]) + }, [dataContext.dates.from, dataContext.dates.to]); - return [data, loading, error] -} \ No newline at end of file + return [data, loading, error]; +} diff --git a/next.config.js b/next.config.js index f2525fe..970af9d 100644 --- a/next.config.js +++ b/next.config.js @@ -1,15 +1,14 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: false, - webpack: (config, { isServer, defaultLoaders }) => { - config.module.rules.push({ - test: /\.svg$/, - use: ['@svgr/webpack'], - }); - return config; - }, - }; - - module.exports = nextConfig; - \ No newline at end of file + reactStrictMode: false, + webpack: (config, { isServer, defaultLoaders }) => { + config.module.rules.push({ + test: /\.svg$/, + use: ['@svgr/webpack'], + }); + return config; + }, +}; + +module.exports = nextConfig; diff --git a/package-lock.json b/package-lock.json index 692fb56..5c5b798 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "moment": "^2.29.4", "next": "13.4.4", "polished": "^4.2.2", + "prettier": "^3.0.0", "react": "18.2.0", "react-date-range": "^1.4.0", "react-dom": "18.2.0", @@ -8070,6 +8071,20 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -15021,6 +15036,11 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, + "prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==" + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", diff --git a/package.json b/package.json index f177a08..424ae95 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,56 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "npx eslint . --fix", + "prettify": "npx prettier --write ." + }, + "eslintConfig": { + "plugins": [ + "react", + "@typescript-eslint" + ], + "extends": [ + "react-app", + "react-app/jest", + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "no-var": "warn", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "vars": "all", + "args": "all", + "ignoreRestSiblings": false, + "argsIgnorePattern": "^_" + } + ], + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "parameter", + "format": [ + "camelCase" + ], + "leadingUnderscore": "allow" + } + ] + }, + "ignorePatterns": [ + "build/**" + ], + "overrides": [ + { + "files": [ + "**/*.stories.*" + ], + "rules": { + "import/no-anonymous-default-export": "off" + } + } + ] }, "dependencies": { "@chakra-ui/icons": "^2.0.19", @@ -27,6 +76,7 @@ "moment": "^2.29.4", "next": "13.4.4", "polished": "^4.2.2", + "prettier": "^3.0.0", "react": "18.2.0", "react-date-range": "^1.4.0", "react-dom": "18.2.0", @@ -46,4 +96,4 @@ "@types/react-gtm-module": "^2.0.1", "@types/react-table": "^7.7.14" } -} \ No newline at end of file +} diff --git a/pre_push_and_pre_commit.sh b/pre_push_and_pre_commit.sh new file mode 100755 index 0000000..31784ad --- /dev/null +++ b/pre_push_and_pre_commit.sh @@ -0,0 +1 @@ +npx prettier --check . && npx eslint . --max-warnings=0 diff --git a/react-table-config.d.ts b/react-table-config.d.ts index 744d657..9d6f924 100644 --- a/react-table-config.d.ts +++ b/react-table-config.d.ts @@ -1,120 +1,120 @@ import { - UseColumnOrderInstanceProps, - UseColumnOrderState, - UseExpandedHooks, - UseExpandedInstanceProps, - UseExpandedOptions, - UseExpandedRowProps, - UseExpandedState, - UseFiltersColumnOptions, - UseFiltersColumnProps, - UseFiltersInstanceProps, - UseFiltersOptions, - UseFiltersState, - UseGlobalFiltersColumnOptions, - UseGlobalFiltersInstanceProps, - UseGlobalFiltersOptions, - UseGlobalFiltersState, - UseGroupByCellProps, - UseGroupByColumnOptions, - UseGroupByColumnProps, - UseGroupByHooks, - UseGroupByInstanceProps, - UseGroupByOptions, - UseGroupByRowProps, - UseGroupByState, - UsePaginationInstanceProps, - UsePaginationOptions, - UsePaginationState, - UseResizeColumnsColumnOptions, - UseResizeColumnsColumnProps, - UseResizeColumnsOptions, - UseResizeColumnsState, - UseRowSelectHooks, - UseRowSelectInstanceProps, - UseRowSelectOptions, - UseRowSelectRowProps, - UseRowSelectState, - UseRowStateCellProps, - UseRowStateInstanceProps, - UseRowStateOptions, - UseRowStateRowProps, - UseRowStateState, - UseSortByColumnOptions, - UseSortByColumnProps, - UseSortByHooks, - UseSortByInstanceProps, - UseSortByOptions, - UseSortByState - } from 'react-table' - - declare module 'react-table' { - // take this file as-is, or comment out the sections that don't apply to your plugin configuration - - export interface TableOptions> - extends UseExpandedOptions, - UseFiltersOptions, - UseGlobalFiltersOptions, - UseGroupByOptions, - UsePaginationOptions, - UseResizeColumnsOptions, - UseRowSelectOptions, - UseRowStateOptions, - UseSortByOptions, - // note that having Record here allows you to add anything to the options, this matches the spirit of the - // underlying js library, but might be cleaner if it's replaced by a more specific type that matches your - // feature set, this is a safe default. - Record {} - - export interface Hooks = Record> - extends UseExpandedHooks, - UseGroupByHooks, - UseRowSelectHooks, - UseSortByHooks {} - - export interface TableInstance = Record> - extends UseColumnOrderInstanceProps, - UseExpandedInstanceProps, - UseFiltersInstanceProps, - UseGlobalFiltersInstanceProps, - UseGroupByInstanceProps, - UsePaginationInstanceProps, - UseRowSelectInstanceProps, - UseRowStateInstanceProps, - UseSortByInstanceProps {} - - export interface TableState = Record> - extends UseColumnOrderState, - UseExpandedState, - UseFiltersState, - UseGlobalFiltersState, - UseGroupByState, - UsePaginationState, - UseResizeColumnsState, - UseRowSelectState, - UseRowStateState, - UseSortByState {} - - export interface ColumnInterface = Record> - extends UseFiltersColumnOptions, - UseGlobalFiltersColumnOptions, - UseGroupByColumnOptions, - UseResizeColumnsColumnOptions, - UseSortByColumnOptions {} - - export interface ColumnInstance = Record> - extends UseFiltersColumnProps, - UseGroupByColumnProps, - UseResizeColumnsColumnProps, - UseSortByColumnProps {} - - export interface Cell = Record, V = any> - extends UseGroupByCellProps, - UseRowStateCellProps {} - - export interface Row = Record> - extends UseExpandedRowProps, - UseGroupByRowProps, - UseRowSelectRowProps, - UseRowStateRowProps {} - } \ No newline at end of file + UseColumnOrderInstanceProps, + UseColumnOrderState, + UseExpandedHooks, + UseExpandedInstanceProps, + UseExpandedOptions, + UseExpandedRowProps, + UseExpandedState, + UseFiltersColumnOptions, + UseFiltersColumnProps, + UseFiltersInstanceProps, + UseFiltersOptions, + UseFiltersState, + UseGlobalFiltersColumnOptions, + UseGlobalFiltersInstanceProps, + UseGlobalFiltersOptions, + UseGlobalFiltersState, + UseGroupByCellProps, + UseGroupByColumnOptions, + UseGroupByColumnProps, + UseGroupByHooks, + UseGroupByInstanceProps, + UseGroupByOptions, + UseGroupByRowProps, + UseGroupByState, + UsePaginationInstanceProps, + UsePaginationOptions, + UsePaginationState, + UseResizeColumnsColumnOptions, + UseResizeColumnsColumnProps, + UseResizeColumnsOptions, + UseResizeColumnsState, + UseRowSelectHooks, + UseRowSelectInstanceProps, + UseRowSelectOptions, + UseRowSelectRowProps, + UseRowSelectState, + UseRowStateCellProps, + UseRowStateInstanceProps, + UseRowStateOptions, + UseRowStateRowProps, + UseRowStateState, + UseSortByColumnOptions, + UseSortByColumnProps, + UseSortByHooks, + UseSortByInstanceProps, + UseSortByOptions, + UseSortByState, +} from 'react-table'; + +declare module 'react-table' { + // take this file as-is, or comment out the sections that don't apply to your plugin configuration + + export interface TableOptions> + extends UseExpandedOptions, + UseFiltersOptions, + UseGlobalFiltersOptions, + UseGroupByOptions, + UsePaginationOptions, + UseResizeColumnsOptions, + UseRowSelectOptions, + UseRowStateOptions, + UseSortByOptions, + // note that having Record here allows you to add anything to the options, this matches the spirit of the + // underlying js library, but might be cleaner if it's replaced by a more specific type that matches your + // feature set, this is a safe default. + Record {} + + export interface Hooks = Record> + extends UseExpandedHooks, + UseGroupByHooks, + UseRowSelectHooks, + UseSortByHooks {} + + export interface TableInstance = Record> + extends UseColumnOrderInstanceProps, + UseExpandedInstanceProps, + UseFiltersInstanceProps, + UseGlobalFiltersInstanceProps, + UseGroupByInstanceProps, + UsePaginationInstanceProps, + UseRowSelectInstanceProps, + UseRowStateInstanceProps, + UseSortByInstanceProps {} + + export interface TableState = Record> + extends UseColumnOrderState, + UseExpandedState, + UseFiltersState, + UseGlobalFiltersState, + UseGroupByState, + UsePaginationState, + UseResizeColumnsState, + UseRowSelectState, + UseRowStateState, + UseSortByState {} + + export interface ColumnInterface = Record> + extends UseFiltersColumnOptions, + UseGlobalFiltersColumnOptions, + UseGroupByColumnOptions, + UseResizeColumnsColumnOptions, + UseSortByColumnOptions {} + + export interface ColumnInstance = Record> + extends UseFiltersColumnProps, + UseGroupByColumnProps, + UseResizeColumnsColumnProps, + UseSortByColumnProps {} + + export interface Cell = Record, V = any> + extends UseGroupByCellProps, + UseRowStateCellProps {} + + export interface Row = Record> + extends UseExpandedRowProps, + UseGroupByRowProps, + UseRowSelectRowProps, + UseRowStateRowProps {} +} diff --git a/styles/components/alert.ts b/styles/components/alert.ts index 930f567..b31689c 100644 --- a/styles/components/alert.ts +++ b/styles/components/alert.ts @@ -2,10 +2,9 @@ const Alert = { baseStyle: { container: { borderRadius: 'xl', - } - }, - variants: { + }, }, + variants: {}, }; export default Alert; diff --git a/styles/components/button.ts b/styles/components/button.ts index eeeeeda..a695fd8 100644 --- a/styles/components/button.ts +++ b/styles/components/button.ts @@ -1,4 +1,4 @@ -import {lighten} from "polished" +import { lighten } from 'polished'; const Button = { baseStyle: { diff --git a/styles/components/input.ts b/styles/components/input.ts index 214233a..b576087 100644 --- a/styles/components/input.ts +++ b/styles/components/input.ts @@ -3,7 +3,7 @@ const Input = { rounded: { borderRadius: '12rem', background: 'red', - } + }, }, }; diff --git a/styles/components/modal.ts b/styles/components/modal.ts index 24f77ed..9018929 100644 --- a/styles/components/modal.ts +++ b/styles/components/modal.ts @@ -7,9 +7,7 @@ export const ModalHeader = { const Modal = { variants: { default: { - dialogContainer: { - - }, + dialogContainer: {}, }, full: { dialogContainer: { diff --git a/styles/components/tabs.ts b/styles/components/tabs.ts index 737ff7d..93259a1 100644 --- a/styles/components/tabs.ts +++ b/styles/components/tabs.ts @@ -1,30 +1,31 @@ -import { tabsAnatomy } from '@chakra-ui/anatomy' -import { createMultiStyleConfigHelpers } from '@chakra-ui/react' -import { mode } from '@chakra-ui/theme-tools' // import utility to set light and dark mode props +import { tabsAnatomy } from '@chakra-ui/anatomy'; +import { createMultiStyleConfigHelpers } from '@chakra-ui/react'; +import { mode } from '@chakra-ui/theme-tools'; // import utility to set light and dark mode props -const { definePartsStyle, defineMultiStyleConfig } = - createMultiStyleConfigHelpers(tabsAnatomy.keys) +const { definePartsStyle, defineMultiStyleConfig } = createMultiStyleConfigHelpers( + tabsAnatomy.keys +); // define a custom variant const colorfulVariant = definePartsStyle((props) => { - const { colorScheme: c } = props // extract colorScheme from component props + const { colorScheme: c } = props; // extract colorScheme from component props return { tab: { border: '2px solid', borderColor: 'transparent', // use colorScheme to change background color with dark and light mode options - bg: "transparent", + bg: 'transparent', //bg: mode(`${c}.300`, `${c}.600`)(props), borderTopRadius: 'lg', borderBottom: 'none', - color: "#444", + color: '#444', fontWeight: 600, //borderBottom: 'none', _selected: { //bg: mode('#fff', 'gray.800')(props), - bg: "#ddd", - color: "#0277ba", + bg: '#ddd', + color: '#0277ba', //borderColor: 'inherit', borderBottom: 'none', mb: '-2px', @@ -40,12 +41,12 @@ const colorfulVariant = definePartsStyle((props) => { borderBottomRadius: 'lg', borderTopRightRadius: 'lg', }, - } -}) + }; +}); const variants = { colorful: colorfulVariant, -} +}; // export the component theme -export const tabsTheme = defineMultiStyleConfig({ variants }) \ No newline at end of file +export const tabsTheme = defineMultiStyleConfig({ variants }); diff --git a/styles/custom.ts b/styles/custom.ts index 0526fef..269c74f 100644 --- a/styles/custom.ts +++ b/styles/custom.ts @@ -1,141 +1,149 @@ import { css } from '@emotion/react'; -export default css` - - .react-datepicker { - font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; - overflow: hidden; - } - - .react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button) { - right: 90px; - } - - .react-datepicker__navigation--previous, - .react-datepicker__navigation--next { - height: 8px; - } - - .react-datepicker__navigation--previous { - border-right-color: #cbd5e0; - - &:hover { - border-right-color: #a0aec0; - } - } - - .react-datepicker__navigation--next { - border-left-color: #cbd5e0; - - &:hover { - border-left-color: #a0aec0; - } - } - - .react-datepicker-wrapper, - .react-datepicker__input-container { - display: block; - } - - .react-datepicker__header { - border-radius: 0; - background: #f7fafc; - } - - .react-datepicker, - .react-datepicker__header, - .react-datepicker__time-container { - border-color: #e2e8f0; - } - - .react-datepicker__current-month, - .react-datepicker-time__header, - .react-datepicker-year-header { - font-size: inherit; - font-weight: 600; - } - - .react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item { - margin: 0 1px 0 0; - height: auto; - padding: 7px 10px; - - &:hover { - background: #edf2f7; - } - } - - .react-datepicker__day:hover { - background: #edf2f7; - } - - .react-datepicker__day--selected, - .react-datepicker__day--in-selecting-range, - .react-datepicker__day--in-range, - .react-datepicker__month-text--selected, - .react-datepicker__month-text--in-selecting-range, - .react-datepicker__month-text--in-range, - .react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected { - background: #3182ce; - font-weight: normal; - - &:hover { - background: #2a69ac; - } - } - - .react-dropdown-select { - border-radius: 12px !important; - } - - .react-dropdown-select:hover, - .react-dropdown-select:focus-within { - border-color: #97ffe4 !important; - } - - .react-dropdown-select-dropdown { - max-height: 300px !important; - height: 300px !important; - background: #0A1F1B !important; - border-color: #061412 !important; - box-shadow: 0px 0px 7px rgb(0 0 0 / 20%) !important; - padding: 6px !important; - } - - .date-range-item { - padding: 6px !important; - } - - .date-range-item:hover { - background: #0f2e29 !important; - } - - .rdrCalendarWrapper { - box-sizing: border-box; - background: transparent !important; - color: #fff !important; - } - .rdrDayDisabled { - background-color: rgb(9 32 27) !important; - } - .rdrMonthAndYearPickers select { - color: #fff !important; - } - .rdrNextPrevButton { - background-color: #194D44 !important; - } - .rdrNextPrevButton i { - border-color: transparent transparent transparent #fff !important; - } - .rdrNextPrevButton.rdrPprevButton i { - border-color: transparent #fff transparent transparent !important; - } - .rdrDayNumber span { - color: #fff !important; - } - .rdrDayToday .rdrDayNumber span:after { - background: #97ffe4 !important; - } +export default css` + .react-datepicker { + font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', + sans-serif; + overflow: hidden; + } + + .react-datepicker__navigation--next--with-time:not( + .react-datepicker__navigation--next--with-today-button + ) { + right: 90px; + } + + .react-datepicker__navigation--previous, + .react-datepicker__navigation--next { + height: 8px; + } + + .react-datepicker__navigation--previous { + border-right-color: #cbd5e0; + + &:hover { + border-right-color: #a0aec0; + } + } + + .react-datepicker__navigation--next { + border-left-color: #cbd5e0; + + &:hover { + border-left-color: #a0aec0; + } + } + + .react-datepicker-wrapper, + .react-datepicker__input-container { + display: block; + } + + .react-datepicker__header { + border-radius: 0; + background: #f7fafc; + } + + .react-datepicker, + .react-datepicker__header, + .react-datepicker__time-container { + border-color: #e2e8f0; + } + + .react-datepicker__current-month, + .react-datepicker-time__header, + .react-datepicker-year-header { + font-size: inherit; + font-weight: 600; + } + + .react-datepicker__time-container + .react-datepicker__time + .react-datepicker__time-box + ul.react-datepicker__time-list + li.react-datepicker__time-list-item { + margin: 0 1px 0 0; + height: auto; + padding: 7px 10px; + + &:hover { + background: #edf2f7; + } + } + + .react-datepicker__day:hover { + background: #edf2f7; + } + + .react-datepicker__day--selected, + .react-datepicker__day--in-selecting-range, + .react-datepicker__day--in-range, + .react-datepicker__month-text--selected, + .react-datepicker__month-text--in-selecting-range, + .react-datepicker__month-text--in-range, + .react-datepicker__time-container + .react-datepicker__time + .react-datepicker__time-box + ul.react-datepicker__time-list + li.react-datepicker__time-list-item--selected { + background: #3182ce; + font-weight: normal; + + &:hover { + background: #2a69ac; + } + } + + .react-dropdown-select { + border-radius: 12px !important; + } + + .react-dropdown-select:hover, + .react-dropdown-select:focus-within { + border-color: #97ffe4 !important; + } + + .react-dropdown-select-dropdown { + max-height: 300px !important; + height: 300px !important; + background: #0a1f1b !important; + border-color: #061412 !important; + box-shadow: 0px 0px 7px rgb(0 0 0 / 20%) !important; + padding: 6px !important; + } + + .date-range-item { + padding: 6px !important; + } + + .date-range-item:hover { + background: #0f2e29 !important; + } + + .rdrCalendarWrapper { + box-sizing: border-box; + background: transparent !important; + color: #fff !important; + } + .rdrDayDisabled { + background-color: rgb(9 32 27) !important; + } + .rdrMonthAndYearPickers select { + color: #fff !important; + } + .rdrNextPrevButton { + background-color: #194d44 !important; + } + .rdrNextPrevButton i { + border-color: transparent transparent transparent #fff !important; + } + .rdrNextPrevButton.rdrPprevButton i { + border-color: transparent #fff transparent transparent !important; + } + .rdrDayNumber span { + color: #fff !important; + } + .rdrDayToday .rdrDayNumber span:after { + background: #97ffe4 !important; + } `; - - diff --git a/styles/fonts.ts b/styles/fonts.ts index d0d5cde..fe53d8a 100644 --- a/styles/fonts.ts +++ b/styles/fonts.ts @@ -5,45 +5,53 @@ import customTheme from './theme'; import onboard from './custom'; export default css` - - @font-face { - font-display: swap; - font-family: Inter; - font-style: normal; - font-weight: 300; - src: url(/fonts/Inter-Light.woff2) format("woff2"), url(/fonts/Inter-Light.woff?v=3.19) format("woff"); - } + @font-face { + font-display: swap; + font-family: Inter; + font-style: normal; + font-weight: 300; + src: + url(/fonts/Inter-Light.woff2) format('woff2'), + url(/fonts/Inter-Light.woff?v=3.19) format('woff'); + } - @font-face { - font-display: swap; - font-family: Inter; - font-style: normal; - font-weight: 400; - src: url(/fonts/Inter-Regular.woff2) format("woff2"), url(/fonts/Inter-Regular.woff?v=3.19) format("woff"); - } + @font-face { + font-display: swap; + font-family: Inter; + font-style: normal; + font-weight: 400; + src: + url(/fonts/Inter-Regular.woff2) format('woff2'), + url(/fonts/Inter-Regular.woff?v=3.19) format('woff'); + } - @font-face { - font-display: swap; - font-family: Inter; - font-style: normal; - font-weight: 700; - src: url(/fonts/Inter-Bold.woff2) format("woff2"), url(/fonts/Inter-Bold.woff?v=3.19) format("woff"); - } + @font-face { + font-display: swap; + font-family: Inter; + font-style: normal; + font-weight: 700; + src: + url(/fonts/Inter-Bold.woff2) format('woff2'), + url(/fonts/Inter-Bold.woff?v=3.19) format('woff'); + } - @font-face { - font-display: swap; - font-family: Teodor; - font-style: normal; - font-weight: 400; - src: url(/fonts/Teodor-Light.woff) format("woff"), url(/fonts/Teodor-Light.woff2) format("woff2"); - } + @font-face { + font-display: swap; + font-family: Teodor; + font-style: normal; + font-weight: 400; + src: + url(/fonts/Teodor-Light.woff) format('woff'), + url(/fonts/Teodor-Light.woff2) format('woff2'); + } - @font-face { - font-display: swap; - font-family: Teodor; - font-style: italic; - font-weight: 400; - src: url(/fonts/Teodor-LightItalic.woff) format("woff"), url(/fonts/Teodor-LightItalic.woff2) format("woff2"); - } - -`; \ No newline at end of file + @font-face { + font-display: swap; + font-family: Teodor; + font-style: italic; + font-weight: 400; + src: + url(/fonts/Teodor-LightItalic.woff) format('woff'), + url(/fonts/Teodor-LightItalic.woff2) format('woff2'); + } +`; diff --git a/styles/global.ts b/styles/global.ts index 80c83b8..646bef5 100644 --- a/styles/global.ts +++ b/styles/global.ts @@ -37,5 +37,4 @@ export const GlobalStyles = css` } ${custom} - `; diff --git a/styles/theme.ts b/styles/theme.ts index 974a616..ced7f11 100644 --- a/styles/theme.ts +++ b/styles/theme.ts @@ -8,8 +8,7 @@ import Drawer from './components/drawer'; import Input from './components/input'; import Tooltip from './components/tooltip'; import Alert from './components/alert'; -import { tabsTheme } from "./components/tabs"; - +import { tabsTheme } from './components/tabs'; const config: ThemeConfig = { initialColorMode: 'dark', @@ -68,6 +67,6 @@ const baseTheme = extendTheme({ }, }); -export const theme = extendTheme(baseTheme) +export const theme = extendTheme(baseTheme); export default baseTheme; diff --git a/tsconfig.json b/tsconfig.json index a914549..e06a445 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -24,18 +20,9 @@ } ], "paths": { - "@/*": [ - "./*" - ] + "@/*": ["./*"] } }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] -} \ No newline at end of file + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/utils/formatting.ts b/utils/formatting.ts index 7e5162e..e398d3e 100644 --- a/utils/formatting.ts +++ b/utils/formatting.ts @@ -7,5 +7,8 @@ export const formatNumber = (num: number | string | boolean, maximumFractionDigi export const formatAddress = (account: string, length?: number): string => { if (!length) return account; - return `${account.substring(0, length)}...${account.substring(account.length - length, account.length)}`; + return `${account.substring(0, length)}...${account.substring( + account.length - length, + account.length + )}`; }; From 38d2af5a490f9151cd2919f536c7edc9967c2153 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Thu, 20 Jul 2023 16:01:09 -0400 Subject: [PATCH 06/59] more clean up --- components/common/header/index.tsx | 2 +- components/home/charts/cumulative-inflow.tsx | 10 +---- .../charts/cumulative-notional-liquidated.tsx | 1 - components/home/charts/cumulative-users.tsx | 7 +--- components/home/charts/date-range.tsx | 4 +- .../home/charts/hlp-liquidator-profit.tsx | 16 ++------ components/home/charts/liquidity.tsx | 40 ------------------- components/home/charts/open-interest.tsx | 7 ---- components/home/charts/trader-profit.tsx | 9 +---- components/home/charts/volume-num-trades.tsx | 13 ------ components/home/main/index.tsx | 3 +- components/home/tables/largest-users.tsx | 9 +---- .../home/tables/liquidated-notional-user.tsx | 6 +-- components/home/tables/user-deposits.tsx | 3 +- components/home/tables/user-trade-count.tsx | 9 +---- 15 files changed, 20 insertions(+), 119 deletions(-) diff --git a/components/common/header/index.tsx b/components/common/header/index.tsx index 7a27ecc..cc4540c 100644 --- a/components/common/header/index.tsx +++ b/components/common/header/index.tsx @@ -1,7 +1,7 @@ 'use client'; import React from 'react'; import NextImg from 'next/image'; -import { Container, Box, Text, Image, Flex, useMediaQuery } from '@chakra-ui/react'; +import { Container, Box, Image, Flex, useMediaQuery } from '@chakra-ui/react'; import * as S from './styles'; const Header = () => { diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 32ba83a..299e9fe 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -1,6 +1,5 @@ import { Bar, - Label, XAxis, YAxis, CartesianGrid, @@ -13,15 +12,10 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { Box, Text, useMediaQuery } from '@chakra-ui/react'; +import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; -import { - yaxisFormatter, - xAxisFormatter, - formatNumberWithOptions, - tooltipFormatterCurrency, -} from '../../../helpers'; +import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency } from '../../../helpers'; import { daily_inflow, cumulative_inflow } from '../../../constants/api'; const REQUESTS = [daily_inflow, cumulative_inflow]; diff --git a/components/home/charts/cumulative-notional-liquidated.tsx b/components/home/charts/cumulative-notional-liquidated.tsx index db312d6..e96a086 100644 --- a/components/home/charts/cumulative-notional-liquidated.tsx +++ b/components/home/charts/cumulative-notional-liquidated.tsx @@ -1,6 +1,5 @@ import { Bar, - Label, XAxis, YAxis, CartesianGrid, diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index 828626b..70c3869 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -28,7 +28,6 @@ export default function CumulativeUsers() { const [isMobile] = useMediaQuery('(max-width: 700px)'); const [formattedData, setFormattedData] = useState([]); - const [unquieKeys, setUnquieKeys] = useState([]); const [dataCumulativeNewUsers, loadingCumulativeNewUsers, errorCumulativeNewUsers] = useRequest( REQUESTS[0], @@ -40,8 +39,6 @@ export default function CumulativeUsers() { [], 'chart_data' ); - const [dataDailyUniqueUsersByCoin, loadingDailyUniqueUsersByCoin, errorDailyUniqueUsersByCoin] = - useRequest(REQUESTS[2], [], 'chart_data'); const loading = loadingCumulativeNewUsers || loadingDailyUniqueUsers; const error = errorCumulativeNewUsers || errorDailyUniqueUsers; @@ -67,10 +64,10 @@ export default function CumulativeUsers() { }; useEffect(() => { - if (!loading) { + if (!loading || !error) { formatData(); } - }, [loading]); + }, [loading, error]); return ( diff --git a/components/home/charts/date-range.tsx b/components/home/charts/date-range.tsx index faa6fc8..01606f8 100644 --- a/components/home/charts/date-range.tsx +++ b/components/home/charts/date-range.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useContext } from 'react'; import Select from 'react-dropdown-select'; -import { Box, Text } from '@chakra-ui/react'; +import { Box } from '@chakra-ui/react'; import moment from 'moment'; import { DateRange } from 'react-date-range'; import strftime from 'strftime'; @@ -13,7 +13,7 @@ const DATA_START_DATE = new Date('2023-05-10'); const DATE_NOW = new Date(); export const DateRangeSelect = () => { - const { dates, setDates } = useContext(DataContext); + const { setDates } = useContext(DataContext); const [selectedDateRangeOption, setSelectedDateRangeOption] = useState(null); const [rangeState, setRangeState] = useState< diff --git a/components/home/charts/hlp-liquidator-profit.tsx b/components/home/charts/hlp-liquidator-profit.tsx index 95b66ce..e632690 100644 --- a/components/home/charts/hlp-liquidator-profit.tsx +++ b/components/home/charts/hlp-liquidator-profit.tsx @@ -42,21 +42,13 @@ export default function HLPProfitLossChart() { [], 'chart_data' ); - const [ - dataCumulativeHLPLiquidatorPNL, - loadingCumulativeHLPLiquidatorPNL, - errorCumulative_HLPLiquidatorPNL, - ] = useRequest(REQUESTS[1], [], 'chart_data'); - const [dataNonLiquidator, loadingNonLiquidator, errorNonLiquidator] = useRequest( - REQUESTS[2], + const [dataCumulativeHLPLiquidatorPNL, loadingCumulativeHLPLiquidatorPNL] = useRequest( + REQUESTS[1], [], 'chart_data' ); - const [ - dataCumulativeNonLiquidatorPNL, - loadingCumulativeNonLiquidatorPNL, - errorCumulative_NonLiquidatorPNL, - ] = useRequest(REQUESTS[3], [], 'chart_data'); + const [dataNonLiquidator] = useRequest(REQUESTS[2], [], 'chart_data'); + const [dataCumulativeNonLiquidatorPNL] = useRequest(REQUESTS[3], [], 'chart_data'); const formatTradingData = (dataHLPLiquidatorPNL: any, dataCumulativeHLPLiquidatorPNL: any) => { let currentProfitCumulative = 0; diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 8bd486d..39b54f8 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -1,12 +1,10 @@ import { - Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, - ComposedChart, Line, LineChart, } from 'recharts'; @@ -24,44 +22,6 @@ import { import { getTokenHex } from '../../../constants/tokens'; import { liquidity_by_coin } from '../../../constants/api'; -type DailyUniqueUsersByCoin = { - time: string; - coin: string; - daily_unique_users: number; - percentage_of_total_users: number; - all: number; -}; - -type UniqueUserTradeData = { - time: string; - daily_unique_users: number; -}; - -type CumulativeNewUsersData = { - time: string; - daily_new_users: number; - cumulative_new_users: number; -}; - -type GroupedTradeData = { - time: Date; - all: number; - daily_unique_users: number; - cumulative_unique_users: number; - unit: string; - [key: string]: number | Date | { [key: string]: number } | string | undefined; -}; - -type TempGroupedTradeData = { - time: Date; - coins: { [key: string]: number }; - all: number; - daily_unique_users: number; - cumulative_unique_users: number; - unit: string; - [key: string]: number | Date | { [key: string]: number } | string | undefined; -}; - const REQUESTS = [liquidity_by_coin]; export default function CumulativeUsers() { diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index c91315a..b0dbd09 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -1,6 +1,4 @@ import { - Bar, - Label, XAxis, YAxis, CartesianGrid, @@ -8,7 +6,6 @@ import { Legend, LineChart, ResponsiveContainer, - ComposedChart, Line, } from 'recharts'; import { useEffect, useState } from 'react'; @@ -25,10 +22,6 @@ import { import { getTokenHex } from '../../../constants/tokens'; import { open_interest } from '../../../constants/api'; -type OpenInterestData = { time: string; coin: string; open_interest: number }; - -type GroupedOpenInterestData = { time: string; [key: string]: number | string }; - const REQUESTS = [open_interest]; export default function VolumeChart() { diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index 83455e6..6df06fb 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -12,18 +12,13 @@ import { XAxis, YAxis, } from 'recharts'; -import { chain, sumBy, sortBy, maxBy, minBy } from 'lodash'; +import { sortBy, maxBy, minBy } from 'lodash'; import { useRequest } from '@/hooks/useRequest'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { cumulative_user_pnl, user_pnl } from '../../../constants/api'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; -import { - yaxisFormatter, - xAxisFormatter, - formatNumberWithOptions, - tooltipFormatterCurrency, -} from '../../../helpers'; +import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency } from '../../../helpers'; const REQUESTS = [cumulative_user_pnl, user_pnl]; diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index cbdd389..3f76a84 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -105,19 +105,6 @@ export default function VolumeChart() { return result; }; - type DailyTradesData = { time: string; daily_trades: number }; - - const formatDailyTradesByTime = ( - dataDailyTrades: DailyTradesData[] - ): { [key: string]: number } => { - const result: { [key: string]: number } = {}; - for (const data of dataDailyTrades) { - result[data.time] = data.daily_trades; - } - return result; - }; - - type CoinTradesData = { coin: string; daily_usd_volume: number; time: string }; type FormattedCoinTradesData = any[]; //{ time: string, all: number, [coin: string]: number }; const formatDailyTradesByCoins = ( diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index 97a6860..a2d19c4 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -1,6 +1,5 @@ 'use client'; -import React, { useState } from 'react'; -import moment from 'moment'; +import React from 'react'; import { Container, Box, Text, Grid, Flex } from '@chakra-ui/react'; import * as S from './styles'; import TopStats from '../charts/top-stats'; diff --git a/components/home/tables/largest-users.tsx b/components/home/tables/largest-users.tsx index 2869078..0688391 100644 --- a/components/home/tables/largest-users.tsx +++ b/components/home/tables/largest-users.tsx @@ -23,11 +23,7 @@ import ChartWrapper from '../../common/chartWrapper'; const REQUESTS = [largest_users_by_usd_volume]; export default function TableComponent() { - const [ - dataLargestUsersByUsdVolume, - loadingLargestUsersByUsdVolume, - errorLargestUsersByUsdVolume, - ] = useRequest(REQUESTS[0], [], 'table_data'); + const [dataLargestUsersByUsdVolume] = useRequest(REQUESTS[0], [], 'table_data'); const [isMobile] = useMediaQuery('(max-width: 700px)'); const columns = React.useMemo( @@ -60,12 +56,11 @@ export default function TableComponent() { page, canPreviousPage, canNextPage, - pageOptions, pageCount, gotoPage, nextPage, previousPage, - state: { pageIndex, pageSize }, + state: { pageIndex }, } = useTable( { columns, diff --git a/components/home/tables/liquidated-notional-user.tsx b/components/home/tables/liquidated-notional-user.tsx index 689e114..7c777ba 100644 --- a/components/home/tables/liquidated-notional-user.tsx +++ b/components/home/tables/liquidated-notional-user.tsx @@ -23,11 +23,7 @@ import { formatAddress } from '../../../utils/formatting'; const REQUESTS = [largest_liquidated_notional_by_user]; export default function TableComponent() { - const [ - dataLargestLiquidatedNotional, - loadingLargestLiquidatedNotional, - errorLargestLiquidatedNotional, - ] = useRequest(REQUESTS[0], [], 'table_data'); + const [dataLargestLiquidatedNotional] = useRequest(REQUESTS[0], [], 'table_data'); const [isMobile] = useMediaQuery('(max-width: 700px)'); const columns = React.useMemo( diff --git a/components/home/tables/user-deposits.tsx b/components/home/tables/user-deposits.tsx index 70abf18..721a3f7 100644 --- a/components/home/tables/user-deposits.tsx +++ b/components/home/tables/user-deposits.tsx @@ -23,8 +23,7 @@ import ChartWrapper from '../../common/chartWrapper'; const REQUESTS = [largest_user_depositors]; export default function TableComponent() { - const [dataLargestUsersByDeposits, loadingLargestUsersByDeposits, errorLargestUsersByDeposits] = - useRequest(REQUESTS[0], [], 'table_data'); + const [dataLargestUsersByDeposits] = useRequest(REQUESTS[0], [], 'table_data'); const [isMobile] = useMediaQuery('(max-width: 700px)'); const columns = React.useMemo( diff --git a/components/home/tables/user-trade-count.tsx b/components/home/tables/user-trade-count.tsx index 64dfb3e..d044da8 100644 --- a/components/home/tables/user-trade-count.tsx +++ b/components/home/tables/user-trade-count.tsx @@ -23,11 +23,7 @@ import { formatAddress } from '../../../utils/formatting'; const REQUESTS = [largest_user_trade_count]; export default function TableComponent() { - const [ - dataLargestUsersByTradeCount, - loadingLargestUsersByTradeCount, - errorLargestUsersByTradeCount, - ] = useRequest(REQUESTS[0], [], 'table_data'); + const [dataLargestUsersByTradeCount] = useRequest(REQUESTS[0], [], 'table_data'); const [isMobile] = useMediaQuery('(max-width: 700px)'); const columns = React.useMemo( @@ -60,12 +56,11 @@ export default function TableComponent() { page, canPreviousPage, canNextPage, - pageOptions, pageCount, gotoPage, nextPage, previousPage, - state: { pageIndex, pageSize }, + state: { pageIndex }, } = useTable( { columns, From bc0d2ec71629e95788b2a66bf6ed5213404c9eb2 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Thu, 20 Jul 2023 16:04:35 -0400 Subject: [PATCH 07/59] cu --- components/home/charts/cumulative-inflow.tsx | 2 +- components/home/charts/cumulative-notional-liquidated.tsx | 1 - components/home/charts/cumulative-users.tsx | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 299e9fe..99d34ed 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -91,7 +91,7 @@ export default function CumulativeInflow() { if (!loading && !errorDailyInflow) { formatData(); } - }, [loading]); + }, [loading, errorDailyInflow]); return ( diff --git a/components/home/charts/cumulative-notional-liquidated.tsx b/components/home/charts/cumulative-notional-liquidated.tsx index e96a086..6269013 100644 --- a/components/home/charts/cumulative-notional-liquidated.tsx +++ b/components/home/charts/cumulative-notional-liquidated.tsx @@ -47,7 +47,6 @@ export default function CumulativeNotionalLiquidated() { const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); - const [formattedCumulativeVolumeData, setFormattedCumulativeVolumeData] = useState([]); const [coinKeys, setCoinKeys] = useState([]); diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index 70c3869..a898e70 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -1,6 +1,5 @@ import { Bar, - Label, XAxis, YAxis, CartesianGrid, From 526c9d2e9f047b8eeff2ff4a257bcea265a2166a Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Mon, 24 Jul 2023 11:08:15 -0400 Subject: [PATCH 08/59] fees --- components/home/charts/fees.tsx | 134 ++++++++++++++++++++++++++++++++ components/home/main/index.tsx | 2 + constants/api.ts | 2 + 3 files changed, 138 insertions(+) create mode 100644 components/home/charts/fees.tsx diff --git a/components/home/charts/fees.tsx b/components/home/charts/fees.tsx new file mode 100644 index 0000000..d29071b --- /dev/null +++ b/components/home/charts/fees.tsx @@ -0,0 +1,134 @@ +import { + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, + Cell, +} from 'recharts'; +import { useEffect, useState } from 'react'; +import { useRequest } from '@/hooks/useRequest'; +import { useMediaQuery } from '@chakra-ui/react'; +import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; +import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency } from '../../../helpers'; +import { total_accrued_fees } from '../../../constants/api'; + +const REQUESTS = [total_accrued_fees]; + +export default function Fees() { + const [isMobile] = useMediaQuery('(max-width: 700px)'); + + const [dailyFeesAccrued, setDailyFeesAccrued] = useState([]); + const [cumulativeFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'total_accrued_fees'); + + interface DailyFeesAccrued { + time: Date; + daily_fees_accrued: number; + } + + interface CumulativeFeesAccrued { + time: Date; + cumulative_fees_accrued: number; + } + + const computeDailyFeesAccrued = ( + cumulativeFeesAccrued: CumulativeFeesAccrued[] + ): DailyFeesAccrued[] => { + const result: DailyFeesAccrued[] = []; + for (let i = 1; i < cumulativeFeesAccrued.length; i++) { + result.push({ + time: cumulativeFeesAccrued[i].time, + daily_fees_accrued: + cumulativeFeesAccrued[i].cumulative_fees_accrued - + cumulativeFeesAccrued[i - 1].cumulative_fees_accrued, + }); + } + + return result; + }; + + const formatData = () => { + const newDailyFeesAccrued = computeDailyFeesAccrued(cumulativeFeesAccrued); + setDailyFeesAccrued(newDailyFeesAccrued); + }; + + useEffect(() => { + if (!loading && !error) { + formatData(); + } + }, [loading, error]); + + return ( + + + + + + + + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + color: '#fff', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + itemSorter={(item) => { + return Number(item.value) * -1; + }} + /> + + + + + + + ); +} diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index a2d19c4..f2fad0d 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -19,6 +19,7 @@ import TableUserDesposits from '../tables/user-deposits'; import TableLiquidatedNotional from '../tables/liquidated-notional-user'; import TableTradeCount from '../tables/user-trade-count'; import Liquidity from '../charts/liquidity'; +import Fees from '../charts/fees'; const Main = () => { return ( @@ -97,6 +98,7 @@ const Main = () => { + diff --git a/constants/api.ts b/constants/api.ts index 8922ad0..1e595c1 100644 --- a/constants/api.ts +++ b/constants/api.ts @@ -49,3 +49,5 @@ export const daily_notional_liquidated_by_leverage_type = export const liquidity_by_coin = '/hyperliquid/liquidity_by_coin'; export const daily_notional_liquidated_by_coin = '/hyperliquid/daily_notional_liquidated_by_coin'; + +export const total_accrued_fees = '/hyperliquid/total_accrued_fees'; From 803c706641ab67dadcb832f013c4d0b780526126 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Mon, 24 Jul 2023 15:44:19 -0400 Subject: [PATCH 09/59] wip --- components/home/charts/fees.tsx | 5 +- components/home/charts/trader-profit.tsx | 10 ++-- .../charts/{volume-non-hlp.tsx => volume.tsx} | 51 +++++++++++++++---- components/home/main/index.tsx | 4 +- constants/api.ts | 1 + 5 files changed, 51 insertions(+), 20 deletions(-) rename components/home/charts/{volume-non-hlp.tsx => volume.tsx} (89%) diff --git a/components/home/charts/fees.tsx b/components/home/charts/fees.tsx index d29071b..3353569 100644 --- a/components/home/charts/fees.tsx +++ b/components/home/charts/fees.tsx @@ -8,13 +8,12 @@ import { ResponsiveContainer, ComposedChart, Line, - Cell, } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; -import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; +import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency } from '../../../helpers'; import { total_accrued_fees } from '../../../constants/api'; @@ -24,7 +23,7 @@ export default function Fees() { const [isMobile] = useMediaQuery('(max-width: 700px)'); const [dailyFeesAccrued, setDailyFeesAccrued] = useState([]); - const [cumulativeFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'total_accrued_fees'); + const [cumulativeFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); interface DailyFeesAccrued { time: Date; diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index 6df06fb..f5dca0f 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react'; import { - Area, Bar, Cell, CartesianGrid, @@ -33,8 +32,9 @@ export default function TradersProfitLossChart() { ); const [dataUserPNL, loadingUserPNL, errorUserPNL] = useRequest(REQUESTS[1], [], 'chart_data'); + const loading = loadingUserPNL || loadingCumulativeUserPNL; + const error = errorUserPNL || errorCumulativeUserPNL; const formatTradingData = () => { - let currentPnlCumulative = 0; let currentProfitCumulative = 0; let currentLossCumulative = 0; @@ -90,15 +90,15 @@ export default function TradersProfitLossChart() { }; useEffect(() => { - if (dataCumulativeUserPNL.length > 0 && dataUserPNL.length > 0) { + if (!loading && !error) { formatTradingData(); } - }, [dataCumulativeUserPNL, dataUserPNL]); + }, [loading, error]); return ( diff --git a/components/home/charts/volume-non-hlp.tsx b/components/home/charts/volume.tsx similarity index 89% rename from components/home/charts/volume-non-hlp.tsx rename to components/home/charts/volume.tsx index 1c635fb..955c4cb 100644 --- a/components/home/charts/volume-non-hlp.tsx +++ b/components/home/charts/volume.tsx @@ -33,6 +33,7 @@ import { daily_usd_volume_by_coin, daily_usd_volume_by_crossed, daily_usd_volume_by_user, + total_volume, } from '../../../constants/api'; const REQUESTS = [ @@ -41,14 +42,14 @@ const REQUESTS = [ daily_usd_volume_by_coin, daily_usd_volume_by_crossed, daily_usd_volume_by_user, + total_volume, ]; export default function VolumeChart() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'VS'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); - const [formattedDataMarin, setFormattedDataMarin] = useState([]); - const [formattedCumulativeVolumeData, setFormattedCumulativeVolumeData] = useState([]); + const [formattedDataMargin, setFormattedDataMargin] = useState([]); const [coinKeys, setCoinKeys] = useState([]); const [dataCumulativeUsdVolume, loadingCumulativeUsdVolume, errorCumulativeUsdVolume] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -67,19 +68,24 @@ export default function VolumeChart() { const [dataDailyUsdVolumeByUser, loadingDailyUsdVolumeByUser, errorDailyUsdVolumeByUser] = useRequest(REQUESTS[4], [], 'chart_data'); + const [dataTotalUSDVolume, loadingDataTotalVolume, errorDataTotalVolume] = + useRequest(REQUESTS[5], [], 'chart_data'); + const loading = loadingCumulativeUsdVolume || loadingDailyUsdVolume || loadingDailyUsdVolumeByCoin || loadingDailyUsdVolumeByCrossed || - loadingDailyUsdVolumeByUser; + loadingDailyUsdVolumeByUser || + loadingDataTotalVolume; const error = errorCumulativeUsdVolume || errorDailyUsdVolume || errorDailyUsdVolumeByCoin || errorDailyUsdVolumeByCrossed || - errorDailyUsdVolumeByUser; + errorDailyUsdVolumeByUser || + errorDataTotalVolume; type CumulativeVolumeData = { cumulative: number; time: string }; @@ -226,7 +232,7 @@ export default function VolumeChart() { ); setCoinKeys(extractUniqueCoins(formattedVolumeByCoins)); setFormattedDataCoins(formattedVolumeByCoins); - setFormattedDataMarin(formattedVolumeByCrossed); + setFormattedDataMargin(formattedVolumeByCrossed); }; const controls = { @@ -245,22 +251,22 @@ export default function VolumeChart() { }; useEffect(() => { - if (!loading) { + if (!loading || error) { formatData(); } - }, [loading]); + }, [loading, error]); return ( @@ -346,6 +352,31 @@ export default function VolumeChart() { /> )} + {dataMode === 'VS' && ( + <> + + + + )} { - + diff --git a/constants/api.ts b/constants/api.ts index 1e595c1..d7ea6cd 100644 --- a/constants/api.ts +++ b/constants/api.ts @@ -51,3 +51,4 @@ export const liquidity_by_coin = '/hyperliquid/liquidity_by_coin'; export const daily_notional_liquidated_by_coin = '/hyperliquid/daily_notional_liquidated_by_coin'; export const total_accrued_fees = '/hyperliquid/total_accrued_fees'; +export const total_volume = '/hyperliquid/total_volume'; From cf46ffbcdf98a2bab2527a6e5bcbd009396d301f Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Tue, 25 Jul 2023 12:56:48 -0400 Subject: [PATCH 10/59] wip --- components/home/charts/hlp.tsx | 453 +++++++++++++++++++++++ components/home/charts/trader-profit.tsx | 8 +- components/home/charts/volume.tsx | 9 +- components/home/main/index.tsx | 6 +- constants/api.ts | 1 + 5 files changed, 465 insertions(+), 12 deletions(-) create mode 100644 components/home/charts/hlp.tsx diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx new file mode 100644 index 0000000..b76c7bd --- /dev/null +++ b/components/home/charts/hlp.tsx @@ -0,0 +1,453 @@ +import { + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + Bar, + BarChart, +} from 'recharts'; +import { useMediaQuery } from '@chakra-ui/react'; +import { useEffect, useState } from 'react'; +import { useRequest } from '@/hooks/useRequest'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import { CHART_HEIGHT } from '../../../constants'; +import { + tooltipFormatter, + xAxisFormatter, + formatterPercent, + yaxisFormatterNumber, +} from '../../../helpers'; +import { getTokenHex } from '../../../constants/tokens'; + +export default function Hlp() { + const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [dataMode, setDataMode] = useState<'ABS' | 'TOT'>('ABS'); + const data = [ + { + time: '2023-07-22', + coin: 'AAVE', + daily_ntl: -24695.366739895835, + daily_ntl_abs: 24695.366739895835, + }, + { time: '2023-07-22', coin: 'APE', daily_ntl: 6690.565926875, daily_ntl_abs: 26976.979054375 }, + { + time: '2023-07-22', + coin: 'APT', + daily_ntl: 7088.425567979167, + daily_ntl_abs: 7088.425567979167, + }, + { + time: '2023-07-22', + coin: 'ARB', + daily_ntl: -142961.36674208334, + daily_ntl_abs: 142961.36674208334, + }, + { + time: '2023-07-22', + coin: 'ATOM', + daily_ntl: -7821.417644645833, + daily_ntl_abs: 7821.417644645833, + }, + { + time: '2023-07-22', + coin: 'AVAX', + daily_ntl: -22351.27923729167, + daily_ntl_abs: 22351.27923729167, + }, + { + time: '2023-07-22', + coin: 'BCH', + daily_ntl: 10347.261835208334, + daily_ntl_abs: 10347.261835208334, + }, + { + time: '2023-07-22', + coin: 'BNB', + daily_ntl: -2153.6072121875, + daily_ntl_abs: 2153.6072121875, + }, + { + time: '2023-07-22', + coin: 'BTC', + daily_ntl: -159247.96129229167, + daily_ntl_abs: 159247.96129229167, + }, + { + time: '2023-07-22', + coin: 'CFX', + daily_ntl: 29395.995763541665, + daily_ntl_abs: 29395.995763541665, + }, + { + time: '2023-07-22', + coin: 'COMP', + daily_ntl: -138.10779104166667, + daily_ntl_abs: 138.10779104166667, + }, + { + time: '2023-07-22', + coin: 'CRV', + daily_ntl: -68507.67506479166, + daily_ntl_abs: 68507.67506479166, + }, + { + time: '2023-07-22', + coin: 'DOGE', + daily_ntl: -20193.003531895833, + daily_ntl_abs: 20193.003531895833, + }, + { + time: '2023-07-22', + coin: 'DYDX', + daily_ntl: -28616.082704375, + daily_ntl_abs: 28616.082704375, + }, + { + time: '2023-07-22', + coin: 'ETH', + daily_ntl: -101322.3473540625, + daily_ntl_abs: 101322.3473540625, + }, + { + time: '2023-07-22', + coin: 'FTM', + daily_ntl: 25041.94942854167, + daily_ntl_abs: 25041.94942854167, + }, + { + time: '2023-07-22', + coin: 'GMX', + daily_ntl: -45063.863167812495, + daily_ntl_abs: 45063.863167812495, + }, + { + time: '2023-07-22', + coin: 'INJ', + daily_ntl: -11738.433360625, + daily_ntl_abs: 12585.570327291667, + }, + { time: '2023-07-22', coin: 'LDO', daily_ntl: -17352.982525, daily_ntl_abs: 17352.982525 }, + { + time: '2023-07-22', + coin: 'LINK', + daily_ntl: -91954.93760666666, + daily_ntl_abs: 91954.93760666666, + }, + { + time: '2023-07-22', + coin: 'LTC', + daily_ntl: -39076.99567708333, + daily_ntl_abs: 39076.99567708333, + }, + { + time: '2023-07-22', + coin: 'MATIC', + daily_ntl: -13557.929645395832, + daily_ntl_abs: 13557.929645395832, + }, + { + time: '2023-07-22', + coin: 'MKR', + daily_ntl: -58675.152068125, + daily_ntl_abs: 58675.152068125, + }, + { time: '2023-07-22', coin: 'OP', daily_ntl: -147964.66079, daily_ntl_abs: 147964.66079 }, + { + time: '2023-07-22', + coin: 'RNDR', + daily_ntl: -59326.29024322917, + daily_ntl_abs: 59326.29024322917, + }, + { + time: '2023-07-22', + coin: 'SNX', + daily_ntl: -39757.906322500006, + daily_ntl_abs: 39757.906322500006, + }, + { + time: '2023-07-22', + coin: 'SOL', + daily_ntl: -144743.25938520834, + daily_ntl_abs: 144743.25938520834, + }, + { + time: '2023-07-22', + coin: 'STX', + daily_ntl: -11695.013407864584, + daily_ntl_abs: 11695.013407864584, + }, + { + time: '2023-07-22', + coin: 'SUI', + daily_ntl: -17981.327026447918, + daily_ntl_abs: 17981.327026447918, + }, + { + time: '2023-07-22', + coin: 'XRP', + daily_ntl: -63650.92103635417, + daily_ntl_abs: 63650.92103635417, + }, + { + time: '2023-07-22', + coin: 'kPEPE', + daily_ntl: -13738.879625760417, + daily_ntl_abs: 13738.879625760417, + }, + { + time: '2023-07-23', + coin: 'AAVE', + daily_ntl: -14259.964020833335, + daily_ntl_abs: 14259.964020833335, + }, + { + time: '2023-07-23', + coin: 'APE', + daily_ntl: 17004.951135833333, + daily_ntl_abs: 17004.951135833333, + }, + { + time: '2023-07-23', + coin: 'APT', + daily_ntl: 7681.8976839479155, + daily_ntl_abs: 7681.8976839479155, + }, + { + time: '2023-07-23', + coin: 'ARB', + daily_ntl: -154179.12537458332, + daily_ntl_abs: 154179.12537458332, + }, + { + time: '2023-07-23', + coin: 'ATOM', + daily_ntl: -9580.938229041667, + daily_ntl_abs: 9580.938229041667, + }, + { + time: '2023-07-23', + coin: 'AVAX', + daily_ntl: -47639.321636458335, + daily_ntl_abs: 47639.321636458335, + }, + { time: '2023-07-23', coin: 'BCH', daily_ntl: 20356.759981875, daily_ntl_abs: 20356.759981875 }, + { time: '2023-07-23', coin: 'BNB', daily_ntl: -5229.61291, daily_ntl_abs: 5229.61291 }, + { + time: '2023-07-23', + coin: 'BTC', + daily_ntl: -22626.143271979163, + daily_ntl_abs: 84629.97167697917, + }, + { time: '2023-07-23', coin: 'CFX', daily_ntl: 28637.50690625, daily_ntl_abs: 28637.50690625 }, + { + time: '2023-07-23', + coin: 'COMP', + daily_ntl: -1924.626443958333, + daily_ntl_abs: 1924.626443958333, + }, + { + time: '2023-07-23', + coin: 'CRV', + daily_ntl: -90672.91292661459, + daily_ntl_abs: 90672.91292661459, + }, + { + time: '2023-07-23', + coin: 'DOGE', + daily_ntl: -16449.22405945833, + daily_ntl_abs: 16449.22405945833, + }, + { + time: '2023-07-23', + coin: 'DYDX', + daily_ntl: -39471.316518541666, + daily_ntl_abs: 39471.316518541666, + }, + { + time: '2023-07-23', + coin: 'ETH', + daily_ntl: -67696.736454375, + daily_ntl_abs: 69032.29530416666, + }, + { time: '2023-07-23', coin: 'FTM', daily_ntl: 24698.520458125, daily_ntl_abs: 24698.520458125 }, + { + time: '2023-07-23', + coin: 'GMX', + daily_ntl: -35914.022444166665, + daily_ntl_abs: 35914.022444166665, + }, + { + time: '2023-07-23', + coin: 'INJ', + daily_ntl: -2506.552681979167, + daily_ntl_abs: 11130.528851354167, + }, + { time: '2023-07-23', coin: 'LDO', daily_ntl: -17352.875905, daily_ntl_abs: 17352.875905 }, + { + time: '2023-07-23', + coin: 'LINK', + daily_ntl: -76143.19305677083, + daily_ntl_abs: 76143.19305677083, + }, + { + time: '2023-07-23', + coin: 'LTC', + daily_ntl: -39612.22826541667, + daily_ntl_abs: 39612.22826541667, + }, + { + time: '2023-07-23', + coin: 'MATIC', + daily_ntl: -12715.908532541667, + daily_ntl_abs: 12715.908532541667, + }, + { + time: '2023-07-23', + coin: 'MKR', + daily_ntl: -71643.27720041666, + daily_ntl_abs: 71643.27720041666, + }, + { + time: '2023-07-23', + coin: 'OP', + daily_ntl: -169959.94850562498, + daily_ntl_abs: 169959.94850562498, + }, + { + time: '2023-07-23', + coin: 'RNDR', + daily_ntl: -52620.743885104166, + daily_ntl_abs: 52620.743885104166, + }, + { + time: '2023-07-23', + coin: 'SNX', + daily_ntl: -37697.82067958333, + daily_ntl_abs: 37697.82067958333, + }, + { + time: '2023-07-23', + coin: 'SOL', + daily_ntl: -155785.38252520832, + daily_ntl_abs: 155785.38252520832, + }, + { + time: '2023-07-23', + coin: 'STX', + daily_ntl: -11593.471571635417, + daily_ntl_abs: 11593.471571635417, + }, + { + time: '2023-07-23', + coin: 'SUI', + daily_ntl: -13438.161510718748, + daily_ntl_abs: 13438.161510718748, + }, + { + time: '2023-07-23', + coin: 'XRP', + daily_ntl: -61726.967644062504, + daily_ntl_abs: 61726.967644062504, + }, + { + time: '2023-07-23', + coin: 'kPEPE', + daily_ntl: -7629.018077135417, + daily_ntl_abs: 9366.293052468749, + }, + ]; + + type HlpPosition = { + time: string; + coin: string; + daily_ntl: number; + daily_ntl_abs: number; + }; + + type HlpAbsolutePositionTotal = { + time: Date; + daily_ntl_abs: number; + }; + + const groupByTime = (data: HlpPosition[]): HlpAbsolutePositionTotal[] => { + const map = new Map(); + + data.forEach((item) => { + let time = item.time; + if (map.has(time)) { + const value = map.get(time)!; + map.set(time, { + time: value.time, + daily_ntl_abs: value.daily_ntl_abs + item.daily_ntl_abs, + }); + } else { + map.set(time, { + time: new Date(time), + daily_ntl_abs: item.daily_ntl_abs, + }); + } + }); + + const result = Array.from(map.values()); + return result; + }; + + const controls = { + toggles: [ + { + text: 'Total absolute notional position', + event: () => setDataMode('ABS'), + active: dataMode === 'ABS', + }, + { + text: 'Total notional position', + event: () => setDataMode('TOT'), + active: dataMode === 'TOT', + }, + ], + }; + + return ( + + + + + + + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + color: '#fff', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + /> + + + + + + ); +} diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index f5dca0f..88f5105 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -33,7 +33,7 @@ export default function TradersProfitLossChart() { const [dataUserPNL, loadingUserPNL, errorUserPNL] = useRequest(REQUESTS[1], [], 'chart_data'); const loading = loadingUserPNL || loadingCumulativeUserPNL; - const error = errorUserPNL || errorCumulativeUserPNL; + const error = errorUserPNL || errorCumulativeUserPNL; const formatTradingData = () => { let currentProfitCumulative = 0; let currentLossCumulative = 0; @@ -96,11 +96,7 @@ export default function TradersProfitLossChart() { }, [loading, error]); return ( - + diff --git a/components/home/charts/volume.tsx b/components/home/charts/volume.tsx index 955c4cb..5765f85 100644 --- a/components/home/charts/volume.tsx +++ b/components/home/charts/volume.tsx @@ -68,8 +68,11 @@ export default function VolumeChart() { const [dataDailyUsdVolumeByUser, loadingDailyUsdVolumeByUser, errorDailyUsdVolumeByUser] = useRequest(REQUESTS[4], [], 'chart_data'); - const [dataTotalUSDVolume, loadingDataTotalVolume, errorDataTotalVolume] = - useRequest(REQUESTS[5], [], 'chart_data'); + const [dataTotalUSDVolume, loadingDataTotalVolume, errorDataTotalVolume] = useRequest( + REQUESTS[5], + [], + 'chart_data' + ); const loading = loadingCumulativeUsdVolume || @@ -352,7 +355,7 @@ export default function VolumeChart() { /> )} - {dataMode === 'VS' && ( + {dataMode === 'VS' && ( <> { return ( @@ -95,10 +96,9 @@ const Main = () => { - - - + + diff --git a/constants/api.ts b/constants/api.ts index d7ea6cd..52acf44 100644 --- a/constants/api.ts +++ b/constants/api.ts @@ -52,3 +52,4 @@ export const daily_notional_liquidated_by_coin = '/hyperliquid/daily_notional_li export const total_accrued_fees = '/hyperliquid/total_accrued_fees'; export const total_volume = '/hyperliquid/total_volume'; +export const hlp_positions = '/hyperliquid/hlp_positions'; From 1095f6b1d0d6ab348c8e809bd7aeac1b188c45ad Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Tue, 25 Jul 2023 14:43:12 -0400 Subject: [PATCH 11/59] fix fees --- components/home/charts/fees.tsx | 84 +++++++++++++++++---------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/components/home/charts/fees.tsx b/components/home/charts/fees.tsx index 3353569..651c459 100644 --- a/components/home/charts/fees.tsx +++ b/components/home/charts/fees.tsx @@ -14,37 +14,39 @@ import { useRequest } from '@/hooks/useRequest'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; -import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency } from '../../../helpers'; +import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency, tooltipLabelFormatter } from '../../../helpers'; import { total_accrued_fees } from '../../../constants/api'; const REQUESTS = [total_accrued_fees]; export default function Fees() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - - const [dailyFeesAccrued, setDailyFeesAccrued] = useState([]); - const [cumulativeFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); + const [formattedData, setFormattedData] = useState([]); + const [dailyFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); interface DailyFeesAccrued { time: Date; - daily_fees_accrued: number; + daily_accrued_fees: number; } - interface CumulativeFeesAccrued { + interface MergedData { time: Date; - cumulative_fees_accrued: number; + daily_accrued_fees: number; + cumulative_accrued_fees: number; } - const computeDailyFeesAccrued = ( - cumulativeFeesAccrued: CumulativeFeesAccrued[] - ): DailyFeesAccrued[] => { - const result: DailyFeesAccrued[] = []; - for (let i = 1; i < cumulativeFeesAccrued.length; i++) { + const makeFormattedData = ( + dailyFeesAccrued: DailyFeesAccrued[] + ): MergedData[] => { + const result = []; + let cumulativeFees = 0; + for (let i = 0; i < dailyFeesAccrued.length; i++) { + const dailyFeeAccrued = dailyFeesAccrued[i].daily_accrued_fees ?? 0; + cumulativeFees += dailyFeeAccrued; result.push({ - time: cumulativeFeesAccrued[i].time, - daily_fees_accrued: - cumulativeFeesAccrued[i].cumulative_fees_accrued - - cumulativeFeesAccrued[i - 1].cumulative_fees_accrued, + time: new Date(dailyFeesAccrued[i].time), + daily_accrued_fees: dailyFeeAccrued, + cumulative_accrued_fees: cumulativeFees }); } @@ -52,8 +54,8 @@ export default function Fees() { }; const formatData = () => { - const newDailyFeesAccrued = computeDailyFeesAccrued(cumulativeFeesAccrued); - setDailyFeesAccrued(newDailyFeesAccrued); + const newFormattedData = makeFormattedData(dailyFeesAccrued); + setFormattedData(newFormattedData); }; useEffect(() => { @@ -63,9 +65,9 @@ export default function Fees() { }, [loading, error]); return ( - + - + - ''} - contentStyle={{ - textAlign: 'left', - background: '#0A1F1B', - borderColor: '#061412', - color: '#fff', - boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', - borderRadius: '26px', - maxHeight: '500px', - }} - itemSorter={(item) => { - return Number(item.value) * -1; - }} - /> + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + color: '#fff', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + itemSorter={(item) => { + return Number(item.value) * -1; + }} + /> From 500327c8220ce705be1cba6dc4a263358f371c36 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Tue, 25 Jul 2023 16:35:36 -0400 Subject: [PATCH 12/59] wip --- .../charts/cumulative-notional-liquidated.tsx | 8 +- components/home/charts/funding-rate.tsx | 4 +- components/home/charts/hlp-exposure.tsx | 190 ++++++++ components/home/charts/hlp.tsx | 453 ------------------ components/home/main/index.tsx | 4 +- 5 files changed, 197 insertions(+), 462 deletions(-) create mode 100644 components/home/charts/hlp-exposure.tsx delete mode 100644 components/home/charts/hlp.tsx diff --git a/components/home/charts/cumulative-notional-liquidated.tsx b/components/home/charts/cumulative-notional-liquidated.tsx index 6269013..79f2613 100644 --- a/components/home/charts/cumulative-notional-liquidated.tsx +++ b/components/home/charts/cumulative-notional-liquidated.tsx @@ -87,8 +87,6 @@ export default function CumulativeNotionalLiquidated() { return result; }; - type VolumeData = { coin: string; daily_usd_volume: number; time: string }; - type LiquidationData = { time: string; leverage_type: 'Cross' | 'Isolated'; @@ -211,10 +209,10 @@ export default function CumulativeNotionalLiquidated() { }; useEffect(() => { - if (!loading) { + if (!loading && !error) { formatData(); } - }, [loading]); + }, [loading, error]); return ( { - return Number(item.value) * -1; + return Math.abs(Number(item.value)); }} /> diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 4dcda07..09e39b4 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -45,7 +45,7 @@ export default function FundingRate() { [coin: string]: number | Date; }; - const groupByTimeAndFilterUnSelected = (data: FundingData[]): GroupedFundingData[] => { + const groupByTimeAndFilterUnselected = (data: FundingData[]): GroupedFundingData[] => { const map = new Map(); const coinFundingTotals = new Map(); @@ -97,7 +97,7 @@ export default function FundingRate() { const formatData = () => { if (dataFundingRate) { - const groupedAndFilteredData = groupByTimeAndFilterUnSelected(dataFundingRate); + const groupedAndFilteredData = groupByTimeAndFilterUnselected(dataFundingRate); setFormattedData(groupedAndFilteredData); } }; diff --git a/components/home/charts/hlp-exposure.tsx b/components/home/charts/hlp-exposure.tsx new file mode 100644 index 0000000..d60769b --- /dev/null +++ b/components/home/charts/hlp-exposure.tsx @@ -0,0 +1,190 @@ +import { + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + Bar, + BarChart, + Cell, +} from 'recharts'; +import { useMediaQuery } from '@chakra-ui/react'; +import { useEffect, useState } from 'react'; +import { useRequest } from '@/hooks/useRequest'; +import ChartWrapper from '../../common/chartWrapper'; +import { CHART_HEIGHT, GREEN, RED } from '../../../constants'; +import { + tooltipFormatterCurrency, + xAxisFormatter, + yaxisFormatterNumber, +} from '../../../helpers'; +import { getTokenHex } from '@/constants/tokens'; +import { hlp_positions } from '@/constants/api'; +const REQUESTS = [hlp_positions]; + +export default function Hlp() { + const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [dataMode, setDataMode] = useState<'COINS' | 'NET'>('NET'); + const [coins, setCoins] = useState([]); + const [dataHlpPositions, loading, error] = useRequest( + REQUESTS[0], + [], + 'chart_data' + ); + const [formattedData, setFormattedData] = useState([]); + + type HlpPosition = { + time: string; + coin: string; + daily_ntl: number; + daily_ntl_abs: number; + }; + + type GroupedData = { + time: Date; + daily_ntl: number; + [coin: string]: any; + }; + + const makeFormattedData = (data: HlpPosition[]): [GroupedData[], string[]] => { + const map = new Map(); + const uniqueCoins = new Set(); + data.forEach((item) => { + let {time, coin, daily_ntl} = item; + if (!map.has(time)) { + let entry = { + time: new Date(time), + daily_ntl: daily_ntl, + [`${coin}`]: daily_ntl, + }; + + map.set(time, entry); + } else { + const existingEntry = map.get(time)!; + const updatedValue = (existingEntry[`${coin}`] || 0) + daily_ntl; + existingEntry[`${coin}`] = updatedValue; + existingEntry.daily_ntl += daily_ntl; + + map.set(time, existingEntry); + } + + if (!uniqueCoins.has(coin)) { + uniqueCoins.add(coin); + } + }); + + const result = Array.from(map.values()); + return [result, Array.from(uniqueCoins)]; + }; + + const controls = { + toggles: [ + { + text: 'Coins', + event: () => setDataMode('COINS'), + active: dataMode === 'COINS', + }, + { + text: 'Net notional position', + event: () => setDataMode('NET'), + active: dataMode === 'NET', + }, + ], + }; + + + const formatData = () => { + if (dataHlpPositions) { + const [groupeData, coins] = makeFormattedData(dataHlpPositions); + setFormattedData(groupeData); + setCoins(coins); + } + }; + + useEffect(() => { + if (!loading && !error) { + formatData(); + } + }, [loading, error]); + + console.log("***", coins, formattedData); + + return ( + + + + + + + ''} + contentStyle={{ + textAlign: 'left', + background: '#0A1F1B', + borderColor: '#061412', + color: '#fff', + boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', + borderRadius: '26px', + maxHeight: '500px', + }} + /> + { + dataMode === 'NET' && + ( + + {( formattedData || []).map((item: GroupedData, i: number) => { + return 0 ? GREEN : RED} />; + })} + + ) + } + { + dataMode === 'COINS' && + ( + coins.map((coin, i) => { + console.log("***", coin, formattedData[0][coin]); + return ( + + ); + }) + ) + } + + + + + ); +} diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx deleted file mode 100644 index b76c7bd..0000000 --- a/components/home/charts/hlp.tsx +++ /dev/null @@ -1,453 +0,0 @@ -import { - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - Bar, - BarChart, -} from 'recharts'; -import { useMediaQuery } from '@chakra-ui/react'; -import { useEffect, useState } from 'react'; -import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; -import { CHART_HEIGHT } from '../../../constants'; -import { - tooltipFormatter, - xAxisFormatter, - formatterPercent, - yaxisFormatterNumber, -} from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; - -export default function Hlp() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<'ABS' | 'TOT'>('ABS'); - const data = [ - { - time: '2023-07-22', - coin: 'AAVE', - daily_ntl: -24695.366739895835, - daily_ntl_abs: 24695.366739895835, - }, - { time: '2023-07-22', coin: 'APE', daily_ntl: 6690.565926875, daily_ntl_abs: 26976.979054375 }, - { - time: '2023-07-22', - coin: 'APT', - daily_ntl: 7088.425567979167, - daily_ntl_abs: 7088.425567979167, - }, - { - time: '2023-07-22', - coin: 'ARB', - daily_ntl: -142961.36674208334, - daily_ntl_abs: 142961.36674208334, - }, - { - time: '2023-07-22', - coin: 'ATOM', - daily_ntl: -7821.417644645833, - daily_ntl_abs: 7821.417644645833, - }, - { - time: '2023-07-22', - coin: 'AVAX', - daily_ntl: -22351.27923729167, - daily_ntl_abs: 22351.27923729167, - }, - { - time: '2023-07-22', - coin: 'BCH', - daily_ntl: 10347.261835208334, - daily_ntl_abs: 10347.261835208334, - }, - { - time: '2023-07-22', - coin: 'BNB', - daily_ntl: -2153.6072121875, - daily_ntl_abs: 2153.6072121875, - }, - { - time: '2023-07-22', - coin: 'BTC', - daily_ntl: -159247.96129229167, - daily_ntl_abs: 159247.96129229167, - }, - { - time: '2023-07-22', - coin: 'CFX', - daily_ntl: 29395.995763541665, - daily_ntl_abs: 29395.995763541665, - }, - { - time: '2023-07-22', - coin: 'COMP', - daily_ntl: -138.10779104166667, - daily_ntl_abs: 138.10779104166667, - }, - { - time: '2023-07-22', - coin: 'CRV', - daily_ntl: -68507.67506479166, - daily_ntl_abs: 68507.67506479166, - }, - { - time: '2023-07-22', - coin: 'DOGE', - daily_ntl: -20193.003531895833, - daily_ntl_abs: 20193.003531895833, - }, - { - time: '2023-07-22', - coin: 'DYDX', - daily_ntl: -28616.082704375, - daily_ntl_abs: 28616.082704375, - }, - { - time: '2023-07-22', - coin: 'ETH', - daily_ntl: -101322.3473540625, - daily_ntl_abs: 101322.3473540625, - }, - { - time: '2023-07-22', - coin: 'FTM', - daily_ntl: 25041.94942854167, - daily_ntl_abs: 25041.94942854167, - }, - { - time: '2023-07-22', - coin: 'GMX', - daily_ntl: -45063.863167812495, - daily_ntl_abs: 45063.863167812495, - }, - { - time: '2023-07-22', - coin: 'INJ', - daily_ntl: -11738.433360625, - daily_ntl_abs: 12585.570327291667, - }, - { time: '2023-07-22', coin: 'LDO', daily_ntl: -17352.982525, daily_ntl_abs: 17352.982525 }, - { - time: '2023-07-22', - coin: 'LINK', - daily_ntl: -91954.93760666666, - daily_ntl_abs: 91954.93760666666, - }, - { - time: '2023-07-22', - coin: 'LTC', - daily_ntl: -39076.99567708333, - daily_ntl_abs: 39076.99567708333, - }, - { - time: '2023-07-22', - coin: 'MATIC', - daily_ntl: -13557.929645395832, - daily_ntl_abs: 13557.929645395832, - }, - { - time: '2023-07-22', - coin: 'MKR', - daily_ntl: -58675.152068125, - daily_ntl_abs: 58675.152068125, - }, - { time: '2023-07-22', coin: 'OP', daily_ntl: -147964.66079, daily_ntl_abs: 147964.66079 }, - { - time: '2023-07-22', - coin: 'RNDR', - daily_ntl: -59326.29024322917, - daily_ntl_abs: 59326.29024322917, - }, - { - time: '2023-07-22', - coin: 'SNX', - daily_ntl: -39757.906322500006, - daily_ntl_abs: 39757.906322500006, - }, - { - time: '2023-07-22', - coin: 'SOL', - daily_ntl: -144743.25938520834, - daily_ntl_abs: 144743.25938520834, - }, - { - time: '2023-07-22', - coin: 'STX', - daily_ntl: -11695.013407864584, - daily_ntl_abs: 11695.013407864584, - }, - { - time: '2023-07-22', - coin: 'SUI', - daily_ntl: -17981.327026447918, - daily_ntl_abs: 17981.327026447918, - }, - { - time: '2023-07-22', - coin: 'XRP', - daily_ntl: -63650.92103635417, - daily_ntl_abs: 63650.92103635417, - }, - { - time: '2023-07-22', - coin: 'kPEPE', - daily_ntl: -13738.879625760417, - daily_ntl_abs: 13738.879625760417, - }, - { - time: '2023-07-23', - coin: 'AAVE', - daily_ntl: -14259.964020833335, - daily_ntl_abs: 14259.964020833335, - }, - { - time: '2023-07-23', - coin: 'APE', - daily_ntl: 17004.951135833333, - daily_ntl_abs: 17004.951135833333, - }, - { - time: '2023-07-23', - coin: 'APT', - daily_ntl: 7681.8976839479155, - daily_ntl_abs: 7681.8976839479155, - }, - { - time: '2023-07-23', - coin: 'ARB', - daily_ntl: -154179.12537458332, - daily_ntl_abs: 154179.12537458332, - }, - { - time: '2023-07-23', - coin: 'ATOM', - daily_ntl: -9580.938229041667, - daily_ntl_abs: 9580.938229041667, - }, - { - time: '2023-07-23', - coin: 'AVAX', - daily_ntl: -47639.321636458335, - daily_ntl_abs: 47639.321636458335, - }, - { time: '2023-07-23', coin: 'BCH', daily_ntl: 20356.759981875, daily_ntl_abs: 20356.759981875 }, - { time: '2023-07-23', coin: 'BNB', daily_ntl: -5229.61291, daily_ntl_abs: 5229.61291 }, - { - time: '2023-07-23', - coin: 'BTC', - daily_ntl: -22626.143271979163, - daily_ntl_abs: 84629.97167697917, - }, - { time: '2023-07-23', coin: 'CFX', daily_ntl: 28637.50690625, daily_ntl_abs: 28637.50690625 }, - { - time: '2023-07-23', - coin: 'COMP', - daily_ntl: -1924.626443958333, - daily_ntl_abs: 1924.626443958333, - }, - { - time: '2023-07-23', - coin: 'CRV', - daily_ntl: -90672.91292661459, - daily_ntl_abs: 90672.91292661459, - }, - { - time: '2023-07-23', - coin: 'DOGE', - daily_ntl: -16449.22405945833, - daily_ntl_abs: 16449.22405945833, - }, - { - time: '2023-07-23', - coin: 'DYDX', - daily_ntl: -39471.316518541666, - daily_ntl_abs: 39471.316518541666, - }, - { - time: '2023-07-23', - coin: 'ETH', - daily_ntl: -67696.736454375, - daily_ntl_abs: 69032.29530416666, - }, - { time: '2023-07-23', coin: 'FTM', daily_ntl: 24698.520458125, daily_ntl_abs: 24698.520458125 }, - { - time: '2023-07-23', - coin: 'GMX', - daily_ntl: -35914.022444166665, - daily_ntl_abs: 35914.022444166665, - }, - { - time: '2023-07-23', - coin: 'INJ', - daily_ntl: -2506.552681979167, - daily_ntl_abs: 11130.528851354167, - }, - { time: '2023-07-23', coin: 'LDO', daily_ntl: -17352.875905, daily_ntl_abs: 17352.875905 }, - { - time: '2023-07-23', - coin: 'LINK', - daily_ntl: -76143.19305677083, - daily_ntl_abs: 76143.19305677083, - }, - { - time: '2023-07-23', - coin: 'LTC', - daily_ntl: -39612.22826541667, - daily_ntl_abs: 39612.22826541667, - }, - { - time: '2023-07-23', - coin: 'MATIC', - daily_ntl: -12715.908532541667, - daily_ntl_abs: 12715.908532541667, - }, - { - time: '2023-07-23', - coin: 'MKR', - daily_ntl: -71643.27720041666, - daily_ntl_abs: 71643.27720041666, - }, - { - time: '2023-07-23', - coin: 'OP', - daily_ntl: -169959.94850562498, - daily_ntl_abs: 169959.94850562498, - }, - { - time: '2023-07-23', - coin: 'RNDR', - daily_ntl: -52620.743885104166, - daily_ntl_abs: 52620.743885104166, - }, - { - time: '2023-07-23', - coin: 'SNX', - daily_ntl: -37697.82067958333, - daily_ntl_abs: 37697.82067958333, - }, - { - time: '2023-07-23', - coin: 'SOL', - daily_ntl: -155785.38252520832, - daily_ntl_abs: 155785.38252520832, - }, - { - time: '2023-07-23', - coin: 'STX', - daily_ntl: -11593.471571635417, - daily_ntl_abs: 11593.471571635417, - }, - { - time: '2023-07-23', - coin: 'SUI', - daily_ntl: -13438.161510718748, - daily_ntl_abs: 13438.161510718748, - }, - { - time: '2023-07-23', - coin: 'XRP', - daily_ntl: -61726.967644062504, - daily_ntl_abs: 61726.967644062504, - }, - { - time: '2023-07-23', - coin: 'kPEPE', - daily_ntl: -7629.018077135417, - daily_ntl_abs: 9366.293052468749, - }, - ]; - - type HlpPosition = { - time: string; - coin: string; - daily_ntl: number; - daily_ntl_abs: number; - }; - - type HlpAbsolutePositionTotal = { - time: Date; - daily_ntl_abs: number; - }; - - const groupByTime = (data: HlpPosition[]): HlpAbsolutePositionTotal[] => { - const map = new Map(); - - data.forEach((item) => { - let time = item.time; - if (map.has(time)) { - const value = map.get(time)!; - map.set(time, { - time: value.time, - daily_ntl_abs: value.daily_ntl_abs + item.daily_ntl_abs, - }); - } else { - map.set(time, { - time: new Date(time), - daily_ntl_abs: item.daily_ntl_abs, - }); - } - }); - - const result = Array.from(map.values()); - return result; - }; - - const controls = { - toggles: [ - { - text: 'Total absolute notional position', - event: () => setDataMode('ABS'), - active: dataMode === 'ABS', - }, - { - text: 'Total notional position', - event: () => setDataMode('TOT'), - active: dataMode === 'TOT', - }, - ], - }; - - return ( - - - - - - - ''} - contentStyle={{ - textAlign: 'left', - background: '#0A1F1B', - borderColor: '#061412', - color: '#fff', - boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', - borderRadius: '26px', - maxHeight: '500px', - }} - /> - - - - - - ); -} diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index f06ee1e..7cad338 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -20,7 +20,7 @@ import TableLiquidatedNotional from '../tables/liquidated-notional-user'; import TableTradeCount from '../tables/user-trade-count'; import Liquidity from '../charts/liquidity'; import Fees from '../charts/fees'; -import Hlp from '../charts/hlp'; +import HlpExposure from '../charts/hlp-exposure'; const Main = () => { return ( @@ -98,7 +98,7 @@ const Main = () => { - + From c8b6937f45a80b02d18016afc562e111378caed5 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Tue, 25 Jul 2023 16:51:24 -0400 Subject: [PATCH 13/59] wip --- components/home/charts/hlp-exposure.tsx | 43 ++++++++++++++++--------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/components/home/charts/hlp-exposure.tsx b/components/home/charts/hlp-exposure.tsx index d60769b..d836692 100644 --- a/components/home/charts/hlp-exposure.tsx +++ b/components/home/charts/hlp-exposure.tsx @@ -25,7 +25,7 @@ const REQUESTS = [hlp_positions]; export default function Hlp() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<'COINS' | 'NET'>('NET'); + const [dataMode, setDataMode] = useState<'COINS' | 'NET'>('COINS'); const [coins, setCoins] = useState([]); const [dataHlpPositions, loading, error] = useRequest( REQUESTS[0], @@ -49,34 +49,46 @@ export default function Hlp() { const makeFormattedData = (data: HlpPosition[]): [GroupedData[], string[]] => { const map = new Map(); - const uniqueCoins = new Set(); + const uniqueTopCoins = new Set(); + data.forEach((item) => { let {time, coin, daily_ntl} = item; + if (!map.has(time)) { - let entry = { + map.set(time, { time: new Date(time), daily_ntl: daily_ntl, [`${coin}`]: daily_ntl, - }; - - map.set(time, entry); + other: 0, + }); } else { const existingEntry = map.get(time)!; - const updatedValue = (existingEntry[`${coin}`] || 0) + daily_ntl; - existingEntry[`${coin}`] = updatedValue; + existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + daily_ntl; existingEntry.daily_ntl += daily_ntl; - - map.set(time, existingEntry); - } - - if (!uniqueCoins.has(coin)) { - uniqueCoins.add(coin); } }); + + map.forEach((entry) => { + const coinEntries = Object.entries(entry).filter(([key]) => key !== 'time' && key !== 'daily_ntl' && key !== 'other'); + const sortedCoinEntries = coinEntries.sort((a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1]))); + const topCoins = sortedCoinEntries.slice(0, 10).map(([coin]) => coin); + const otherCoins = sortedCoinEntries.slice(10); + topCoins.forEach(coin => uniqueTopCoins.add(coin)); + + let otherTotal = 0; + otherCoins.forEach(([coin, value]) => { + otherTotal += value; + delete entry[coin]; + }); + entry.Other = otherTotal; + }); + const result = Array.from(map.values()); - return [result, Array.from(uniqueCoins)]; + uniqueTopCoins.add("Other"); + return [result, Array.from(uniqueTopCoins)]; }; + const controls = { toggles: [ @@ -165,7 +177,6 @@ export default function Hlp() { dataMode === 'COINS' && ( coins.map((coin, i) => { - console.log("***", coin, formattedData[0][coin]); return ( Date: Tue, 25 Jul 2023 17:15:53 -0400 Subject: [PATCH 14/59] wip --- components/common/chartWrapper/index.tsx | 83 +++--- components/home/charts/cumulative-inflow.tsx | 9 +- components/home/charts/cumulative-users.tsx | 9 +- components/home/charts/fees.tsx | 15 +- components/home/charts/funding-rate.tsx | 9 +- components/home/charts/hlp-exposure.tsx | 253 +++++++++++++----- .../charts/{volume.tsx => retail-volume.tsx} | 46 +--- components/home/charts/trader-profit.tsx | 9 +- components/home/main/index.tsx | 4 +- constants/api.ts | 1 + helpers/index.ts | 5 + 11 files changed, 285 insertions(+), 158 deletions(-) rename components/home/charts/{volume.tsx => retail-volume.tsx} (90%) diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index c8cfd70..9e8151c 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -51,43 +51,56 @@ function ChartWrapper(props: any) { justifyContent='space-between' flexDirection={{ xs: 'column', md: 'row' }} > - - {title} - - - - {controls && - controls.toggles && - controls.toggles.length > 0 && - controls.toggles.map((toggle: Toggle, index: number) => { - return ( - - ); - })} - - - - {coinSelectors && ( + {title} + + {controls && ( + + + {controls.toggles && + controls.toggles.length > 0 && + controls.toggles.map((toggle: Toggle, index: number) => { + return ( + + ); + })} + + + )} + {coinSelectors && ( + - }> + } + variant='primary' + fontSize={'14px'} + fontWeight={'bold'} + > Select coins - )} - + + )} {loading && } diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 99d34ed..c6bf860 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -15,7 +15,12 @@ import { useRequest } from '@/hooks/useRequest'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; -import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency } from '../../../helpers'; +import { + yaxisFormatter, + xAxisFormatter, + tooltipFormatterCurrency, + dateTooltipFormatter, +} from '../../../helpers'; import { daily_inflow, cumulative_inflow } from '../../../constants/api'; const REQUESTS = [daily_inflow, cumulative_inflow]; @@ -123,7 +128,7 @@ export default function CumulativeInflow() { /> ''} + labelFormatter={dateTooltipFormatter} contentStyle={{ textAlign: 'left', background: '#0A1F1B', diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index a898e70..d303870 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -14,7 +14,12 @@ import { useRequest } from '@/hooks/useRequest'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; -import { xAxisFormatter, yaxisFormatterNumber, tooltipFormatter } from '../../../helpers'; +import { + xAxisFormatter, + yaxisFormatterNumber, + tooltipFormatter, + dateTooltipFormatter, +} from '../../../helpers'; import { cumulative_new_users, daily_unique_users, @@ -98,7 +103,7 @@ export default function CumulativeUsers() { /> ''} + labelFormatter={dateTooltipFormatter} contentStyle={{ textAlign: 'left', background: '#0A1F1B', diff --git a/components/home/charts/fees.tsx b/components/home/charts/fees.tsx index 651c459..f4cd384 100644 --- a/components/home/charts/fees.tsx +++ b/components/home/charts/fees.tsx @@ -14,7 +14,12 @@ import { useRequest } from '@/hooks/useRequest'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; -import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency, tooltipLabelFormatter } from '../../../helpers'; +import { + yaxisFormatter, + xAxisFormatter, + tooltipFormatterCurrency, + dateTooltipFormatter, +} from '../../../helpers'; import { total_accrued_fees } from '../../../constants/api'; const REQUESTS = [total_accrued_fees]; @@ -35,9 +40,7 @@ export default function Fees() { cumulative_accrued_fees: number; } - const makeFormattedData = ( - dailyFeesAccrued: DailyFeesAccrued[] - ): MergedData[] => { + const makeFormattedData = (dailyFeesAccrued: DailyFeesAccrued[]): MergedData[] => { const result = []; let cumulativeFees = 0; for (let i = 0; i < dailyFeesAccrued.length; i++) { @@ -46,7 +49,7 @@ export default function Fees() { result.push({ time: new Date(dailyFeesAccrued[i].time), daily_accrued_fees: dailyFeeAccrued, - cumulative_accrued_fees: cumulativeFees + cumulative_accrued_fees: cumulativeFees, }); } @@ -114,7 +117,7 @@ export default function Fees() { /> ''} + labelFormatter={dateTooltipFormatter} contentStyle={{ textAlign: 'left', background: '#0A1F1B', diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 09e39b4..73fdba9 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -13,7 +13,12 @@ import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT } from '../../../constants'; -import { tooltipFormatter, xAxisFormatter, formatterPercent } from '../../../helpers'; +import { + tooltipFormatter, + xAxisFormatter, + formatterPercent, + dateTooltipFormatter, +} from '../../../helpers'; import { getTokenHex } from '../../../constants/tokens'; import { funding_rate } from '../../../constants/api'; @@ -162,7 +167,7 @@ export default function FundingRate() { /> ''} + labelFormatter={dateTooltipFormatter} contentStyle={{ textAlign: 'left', background: '#0A1F1B', diff --git a/components/home/charts/hlp-exposure.tsx b/components/home/charts/hlp-exposure.tsx index d836692..edd0048 100644 --- a/components/home/charts/hlp-exposure.tsx +++ b/components/home/charts/hlp-exposure.tsx @@ -6,34 +6,48 @@ import { Legend, ResponsiveContainer, Bar, - BarChart, Cell, + Line, + ComposedChart, } from 'recharts'; -import { useMediaQuery } from '@chakra-ui/react'; +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; -import { CHART_HEIGHT, GREEN, RED } from '../../../constants'; +import { BRIGHT_GREEN, CHART_HEIGHT, GREEN, RED } from '../../../constants'; import { + dateTooltipFormatter, tooltipFormatterCurrency, xAxisFormatter, yaxisFormatterNumber, } from '../../../helpers'; import { getTokenHex } from '@/constants/tokens'; -import { hlp_positions } from '@/constants/api'; -const REQUESTS = [hlp_positions]; +import { asset_ctxs, hlp_liquidator_pnl, hlp_positions } from '@/constants/api'; +const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; export default function Hlp() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<'COINS' | 'NET'>('COINS'); + const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('NET'); const [coins, setCoins] = useState([]); - const [dataHlpPositions, loading, error] = useRequest( + const [dataHlpPositions, loadingDataHlpPositions, errorDataHlpPositions] = useRequest( REQUESTS[0], [], 'chart_data' ); + const [assetCtxs, loadingAssetCtxs, errorAssetCtxs] = useRequest(REQUESTS[1], [], 'chart_data'); + const [dataHlpPnL, loadingHlpLiquidatorPNL, errorHlpLiquidatorPNL] = useRequest( + REQUESTS[2], + [], + 'chart_data' + ); + const [ethOraclePxs, setEthOraclePxs] = useState>(new Map()); + const [hlpPnL, setHlpPnL] = useState>(new Map()); + const [formattedHlpPnL, setFormattedHlpPnL] = useState([]); const [formattedData, setFormattedData] = useState([]); + const loading = loadingAssetCtxs | loadingDataHlpPositions | loadingHlpLiquidatorPNL; + const error = errorAssetCtxs | errorDataHlpPositions | errorHlpLiquidatorPNL; + type HlpPosition = { time: string; coin: string; @@ -41,41 +55,101 @@ export default function Hlp() { daily_ntl_abs: number; }; + type AssetCtx = { + time: string; + coin: string; + avg_oracle_px: number; + avg_open_interest: number; + }; + + type HlpPnl = { + time: Date; + pnl: number; + cumulativePnl: number; + }; + type GroupedData = { time: Date; daily_ntl: number; [coin: string]: any; + hedged_pnl: number; }; - const makeFormattedData = (data: HlpPosition[]): [GroupedData[], string[]] => { + const getEthOraclePxs = (assetCtxs: AssetCtx[]): Map => { + const map = new Map(); + assetCtxs.forEach((item) => { + if (item.coin === 'ETH') { + map.set(item.time, item.avg_oracle_px); + } + }); + return map; + }; + + const makeHlpPnl = ( + dataHlpPnL: { + time: string; + total_pnl: number; + }[] + ): Map => { + const map = new Map(); + let cumulativePnl = 0; + dataHlpPnL.forEach((item) => { + cumulativePnl += item.total_pnl; + map.set(item.time, { + time: new Date(item.time), + pnl: item.total_pnl, + cumulativePnl, + }); + }); + return map; + }; + + const makeFormattedData = (hlpPositions: HlpPosition[]): [GroupedData[], string[]] => { const map = new Map(); const uniqueTopCoins = new Set(); - - data.forEach((item) => { - let {time, coin, daily_ntl} = item; - + + let ethOraclePxPrev: number | null | undefined = null; + let prevTime: string | null = null; + hlpPositions.forEach((item: HlpPosition) => { + let { time, coin, daily_ntl } = item; if (!map.has(time)) { + const pnl = hlpPnL.get(time)?.pnl; + const ethOraclePx = ethOraclePxs.get(time); + let hedgedPnl = pnl ?? 0; + let prevDayNtlPosition = prevTime ? map.get(prevTime)?.daily_ntl : null; + if (ethOraclePxPrev && ethOraclePx && prevDayNtlPosition) { + const ethPxChange = 1 - ethOraclePxPrev / ethOraclePx; + const ethPnL = prevDayNtlPosition * ethPxChange; + hedgedPnl += ethPnL; + } map.set(time, { time: new Date(time), daily_ntl: daily_ntl, [`${coin}`]: daily_ntl, - other: 0, + hedged_pnl: hedgedPnl, + Other: 0, }); + ethOraclePxPrev = ethOraclePx; + prevTime = time; } else { const existingEntry = map.get(time)!; existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + daily_ntl; existingEntry.daily_ntl += daily_ntl; } }); - + map.forEach((entry) => { - const coinEntries = Object.entries(entry).filter(([key]) => key !== 'time' && key !== 'daily_ntl' && key !== 'other'); - const sortedCoinEntries = coinEntries.sort((a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1]))); + const coinEntries = Object.entries(entry).filter( + ([key]) => key !== 'time' && key !== 'daily_ntl' && key !== 'hedged_pnl' && key !== 'other' + ); + const sortedCoinEntries = coinEntries.sort( + (a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1])) + ); const topCoins = sortedCoinEntries.slice(0, 10).map(([coin]) => coin); const otherCoins = sortedCoinEntries.slice(10); - topCoins.forEach(coin => uniqueTopCoins.add(coin)); - + topCoins.forEach((coin) => uniqueTopCoins.add(coin)); + let otherTotal = 0; otherCoins.forEach(([coin, value]) => { otherTotal += value; @@ -83,34 +157,47 @@ export default function Hlp() { }); entry.Other = otherTotal; }); - + const result = Array.from(map.values()); - uniqueTopCoins.add("Other"); + uniqueTopCoins.add('Other'); return [result, Array.from(uniqueTopCoins)]; }; - const controls = { toggles: [ { - text: 'Coins', + text: 'Net notional position', + event: () => setDataMode('NET'), + active: dataMode === 'NET', + }, + { + text: 'By coins', event: () => setDataMode('COINS'), active: dataMode === 'COINS', }, { - text: 'Net notional position', - event: () => setDataMode('NET'), - active: dataMode === 'NET', + text: 'Net PnL', + event: () => setDataMode('PNL'), + active: dataMode === 'PNL', + }, + { + text: 'Hedged PnL', + event: () => setDataMode('HEDGED'), + active: dataMode === 'HEDGED', }, ], }; - const formatData = () => { - if (dataHlpPositions) { - const [groupeData, coins] = makeFormattedData(dataHlpPositions); - setFormattedData(groupeData); + if (dataHlpPositions && assetCtxs && dataHlpPnL) { + const newEthOraclePxs = getEthOraclePxs(assetCtxs); + setEthOraclePxs(newEthOraclePxs); + const newHlpPnL = makeHlpPnl(dataHlpPnL); + setFormattedHlpPnL(Array.from(newHlpPnL.values())); + setHlpPnL(newHlpPnL); + const [groupedData, coins] = makeFormattedData(dataHlpPositions); setCoins(coins); + setFormattedData(groupedData); } }; @@ -118,17 +205,12 @@ export default function Hlp() { if (!loading && !error) { formatData(); } - }, [loading, error]); - - console.log("***", coins, formattedData); + }, [loading, error, hlpPnL]); return ( - + - + ''} + labelFormatter={dateTooltipFormatter} contentStyle={{ textAlign: 'left', background: '#0A1F1B', @@ -155,11 +237,13 @@ export default function Hlp() { borderRadius: '26px', maxHeight: '500px', }} + cursor={{ fill: '#0A1F1B' }} + itemSorter={(item) => { + return Math.abs(Number(item.value)) * -1; + }} /> - { - dataMode === 'NET' && - ( - - {( formattedData || []).map((item: GroupedData, i: number) => { + {(formattedData || []).map((item: GroupedData, i: number) => { return 0 ? GREEN : RED} />; })} - ) - } - { - dataMode === 'COINS' && - ( - coins.map((coin, i) => { - return ( - - ); - }) - ) - } + )} + {dataMode === 'COINS' && + coins.map((coin, i) => { + return ( + + ); + })} + {dataMode === 'HEDGED' && ( + + {(formattedData || []).map((item: GroupedData, i: number) => { + return 0 ? GREEN : RED} />; + })} + + )} + {dataMode === 'PNL' && ( + <> + + + {formattedHlpPnL.map((item: HlpPnl, i: number) => { + return 0 ? GREEN : RED} />; + })} + + + )} - + + + {dataMode === 'COINS' && ( + Top 10 Coins grouped daily and remaining coins grouped by Other + )} + ); } diff --git a/components/home/charts/volume.tsx b/components/home/charts/retail-volume.tsx similarity index 90% rename from components/home/charts/volume.tsx rename to components/home/charts/retail-volume.tsx index 5765f85..75959dd 100644 --- a/components/home/charts/volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -33,7 +33,6 @@ import { daily_usd_volume_by_coin, daily_usd_volume_by_crossed, daily_usd_volume_by_user, - total_volume, } from '../../../constants/api'; const REQUESTS = [ @@ -42,12 +41,11 @@ const REQUESTS = [ daily_usd_volume_by_coin, daily_usd_volume_by_crossed, daily_usd_volume_by_user, - total_volume, ]; -export default function VolumeChart() { +export default function RetailVolumeChart() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'VS'>('COINS'); + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); const [coinKeys, setCoinKeys] = useState([]); @@ -68,27 +66,18 @@ export default function VolumeChart() { const [dataDailyUsdVolumeByUser, loadingDailyUsdVolumeByUser, errorDailyUsdVolumeByUser] = useRequest(REQUESTS[4], [], 'chart_data'); - const [dataTotalUSDVolume, loadingDataTotalVolume, errorDataTotalVolume] = useRequest( - REQUESTS[5], - [], - 'chart_data' - ); - const loading = loadingCumulativeUsdVolume || loadingDailyUsdVolume || loadingDailyUsdVolumeByCoin || loadingDailyUsdVolumeByCrossed || - loadingDailyUsdVolumeByUser || - loadingDataTotalVolume; - + loadingDailyUsdVolumeByUser; const error = errorCumulativeUsdVolume || errorDailyUsdVolume || errorDailyUsdVolumeByCoin || errorDailyUsdVolumeByCrossed || - errorDailyUsdVolumeByUser || - errorDataTotalVolume; + errorDailyUsdVolumeByUser; type CumulativeVolumeData = { cumulative: number; time: string }; @@ -261,7 +250,7 @@ export default function VolumeChart() { return ( )} - {dataMode === 'VS' && ( - <> - - - - )} ''} + labelFormatter={dateTooltipFormatter} contentStyle={{ textAlign: 'left', background: '#0A1F1B', diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index 7cad338..37faac1 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { Container, Box, Text, Grid, Flex } from '@chakra-ui/react'; import * as S from './styles'; import TopStats from '../charts/top-stats'; -import VolumeChart from '../charts/volume'; +import RetailVolumeChart from '../charts/retail-volume'; import VolumeNumTrades from '../charts/volume-num-trades'; import OpenInterestChart from '../charts/open-interest'; import TradersProfitLossChart from '../charts/trader-profit'; @@ -77,7 +77,7 @@ const Main = () => { - + diff --git a/constants/api.ts b/constants/api.ts index 52acf44..7d1fb8a 100644 --- a/constants/api.ts +++ b/constants/api.ts @@ -53,3 +53,4 @@ export const daily_notional_liquidated_by_coin = '/hyperliquid/daily_notional_li export const total_accrued_fees = '/hyperliquid/total_accrued_fees'; export const total_volume = '/hyperliquid/total_volume'; export const hlp_positions = '/hyperliquid/hlp_positions'; +export const asset_ctxs = '/hyperliquid/asset_ctxs'; diff --git a/helpers/index.ts b/helpers/index.ts index 8ec426a..a3e21c9 100644 --- a/helpers/index.ts +++ b/helpers/index.ts @@ -185,6 +185,11 @@ export const tooltipFormatterCurrency = (value: number | string): string => { return formatNumberWithOptions(Number(value), { currency: true, compact: true }); }; +export const dateTooltipFormatter = (label: any) => { + const date = new Date(label); + return `Date : ${date.toLocaleDateString()}`; +}; + export const tooltipFormatterPercent = (value: number): string => { return value.toFixed(2) + '%'; }; From 3b44ee190ed906dbda323b664def9f695b5387db Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Wed, 26 Jul 2023 11:11:42 -0400 Subject: [PATCH 15/59] naming --- components/home/charts/cumulative-inflow.tsx | 4 ++-- components/home/charts/cumulative-users.tsx | 4 ++-- components/home/charts/fees.tsx | 4 ++-- components/home/charts/funding-rate.tsx | 4 ++-- components/home/charts/hlp-exposure.tsx | 4 ++-- components/home/charts/hlp-liquidator-profit.tsx | 4 ++-- components/home/charts/trader-profit.tsx | 4 ++-- helpers/index.ts | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index c6bf860..320ed92 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -19,7 +19,7 @@ import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency, - dateTooltipFormatter, + tooltopFormatterDate, } from '../../../helpers'; import { daily_inflow, cumulative_inflow } from '../../../constants/api'; @@ -128,7 +128,7 @@ export default function CumulativeInflow() { /> ''} + labelFormatter={tooltopFormatterDate} contentStyle={{ textAlign: 'left', background: '#0A1F1B', diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index 505a3fc..de423be 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -21,7 +21,7 @@ import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency, - dateTooltipFormatter, + tooltopFormatterDate, } from '../../../helpers'; const REQUESTS = [cumulative_user_pnl, user_pnl]; @@ -128,7 +128,7 @@ export default function TradersProfitLossChart() { /> { return formatNumberWithOptions(Number(value), { currency: true, compact: true }); }; -export const dateTooltipFormatter = (label: any) => { +export const tooltopFormatterDate = (label: any) => { const date = new Date(label); return `Date : ${date.toLocaleDateString()}`; }; From da25707d794bafb68955631d5e0756fdcffe92bd Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Wed, 26 Jul 2023 15:29:10 -0400 Subject: [PATCH 16/59] fixes --- components/home/charts/cumulative-inflow.tsx | 4 +- components/home/charts/cumulative-users.tsx | 4 +- components/home/charts/date-range.tsx | 29 +-- components/home/charts/fees.tsx | 8 +- components/home/charts/funding-rate.tsx | 4 +- .../home/charts/hlp-liquidator-profit.tsx | 216 ------------------ .../home/charts/{hlp-exposure.tsx => hlp.tsx} | 96 +++++--- ...notional-liquidated.tsx => liquidator.tsx} | 207 +++++++++++++---- components/home/charts/trader-profit.tsx | 7 +- components/home/main/index.tsx | 6 +- constants/index.ts | 2 + helpers/index.ts | 11 +- 12 files changed, 256 insertions(+), 338 deletions(-) delete mode 100644 components/home/charts/hlp-liquidator-profit.tsx rename components/home/charts/{hlp-exposure.tsx => hlp.tsx} (84%) rename components/home/charts/{cumulative-notional-liquidated.tsx => liquidator.tsx} (63%) diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 320ed92..2acf73d 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -19,7 +19,7 @@ import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency, - tooltopFormatterDate, + tooltipFormatterDate, } from '../../../helpers'; import { daily_inflow, cumulative_inflow } from '../../../constants/api'; @@ -128,7 +128,7 @@ export default function CumulativeInflow() { /> { onChange([item.selection.startDate, item.selection.endDate]); }; - const customContentRenderer = ({ props, state }: any) => { - const start = dataRange.fromValue && dataRange.fromValue.toISOString().slice(0, 10); - const end = dataRange.toValue && (dataRange.toValue as Date).toISOString().slice(0, 10); - + const customContentRenderer = () => { return (
{dataRange.fromValue && @@ -119,31 +116,9 @@ export const DateRangeSelect = () => { ); }; - const customDropdownRenderer = ({ props, state, methods }: any) => { - const regexp = new RegExp(state.search, 'i'); - + const customDropdownRenderer = ({ props, state }: any) => { return ( - {/* - {props.options - .filter((item: any) => regexp.test(item[props.searchBy] || item[props.labelField])) - .map((option: any, index: any) => { - if (!props.keepSelectedInList && methods.isSelected(option)) { - return null; - } - - return ( - null : () => onSelectItem(option)} - className={option.id === selectedDateRangeOption ? 'date-range-item selected' : 'date-range-item'} - > - {option[props.labelField]} - - ); - })} - */} (null); - const [chartDataNonLiquidator, setChartDataNonLiquidator] = useState(null); - const [dataMode, setDataMode] = useState<'HLP' | 'NON_HLP'>('HLP'); - const [dataHLPLiquidatorPNL, loadingHLPLiquidatorPNL, errorHLPLiquidatorPNL] = useRequest( - REQUESTS[0], - [], - 'chart_data' - ); - const [dataCumulativeHLPLiquidatorPNL, loadingCumulativeHLPLiquidatorPNL] = useRequest( - REQUESTS[1], - [], - 'chart_data' - ); - const [dataNonLiquidator] = useRequest(REQUESTS[2], [], 'chart_data'); - const [dataCumulativeNonLiquidatorPNL] = useRequest(REQUESTS[3], [], 'chart_data'); - - const formatTradingData = (dataHLPLiquidatorPNL: any, dataCumulativeHLPLiquidatorPNL: any) => { - let currentProfitCumulative = 0; - let currentLossCumulative = 0; - - let data = sortBy(dataHLPLiquidatorPNL, (i) => Date.parse(i.time)).map((dataItem, index) => { - const cumulative_pnl = dataCumulativeHLPLiquidatorPNL[index]?.cumulative_pnl || 0; - return { - cumulative_pnl: cumulative_pnl, - total_pnl: dataItem.total_pnl, - timestamp: new Date(dataItem.time), - profit_cumulative: cumulative_pnl > 0 ? cumulative_pnl : 0, - loss_cumulative: cumulative_pnl < 0 ? cumulative_pnl : 0, - unit: 'single', - }; - }); - - let result; - - if (data && data.length > 0) { - const maxProfit = maxBy(data, (item) => item.profit_cumulative)?.profit_cumulative || 0; - const maxLoss = minBy(data, (item) => item.loss_cumulative)?.loss_cumulative || 0; - const maxPnl = maxBy(data, (item) => item.total_pnl)?.total_pnl || 0; - const minPnl = minBy(data, (item) => item.total_pnl)?.total_pnl || 0; - const maxCurrentCumulativePnl = - maxBy(data, (item) => item.cumulative_pnl)?.cumulative_pnl || 0; - const minCurrentCumulativePnl = - minBy(data, (item) => item.cumulative_pnl)?.cumulative_pnl || 0; - - const stats = { - maxProfit, - maxLoss, - currentProfitCumulative, - currentLossCumulative, - maxCurrentCumulativeProfitLoss: Math.max(currentProfitCumulative, -currentLossCumulative), - minAbsPnl: minPnl, - maxAbsPnl: Math.max(Math.abs(maxPnl), Math.abs(minPnl)), - maxAbsCumulativePnl: Math.max( - Math.abs(maxCurrentCumulativePnl), - Math.abs(minCurrentCumulativePnl) - ), - }; - result = { - data, - stats, - }; - } - return result; - }; - - useEffect(() => { - if ( - dataHLPLiquidatorPNL.length > 0 && - dataCumulativeHLPLiquidatorPNL.length > 0 && - dataNonLiquidator.length > 0 && - dataCumulativeNonLiquidatorPNL.length > 0 - ) { - const formattedTradingData = formatTradingData( - dataHLPLiquidatorPNL, - dataCumulativeHLPLiquidatorPNL - ); - const formattedDataNonLiquidator = formatTradingData( - dataNonLiquidator, - dataCumulativeNonLiquidatorPNL - ); - setChartDataLiquidator(formattedTradingData); - setChartDataNonLiquidator(formattedDataNonLiquidator); - } - }, [ - dataHLPLiquidatorPNL, - dataCumulativeHLPLiquidatorPNL, - dataNonLiquidator, - dataCumulativeNonLiquidatorPNL, - ]); - - const controls = { - toggles: [ - { - text: 'HLP', - event: () => setDataMode('HLP'), - active: dataMode === 'HLP', - }, - { - text: 'Liquidator', - event: () => setDataMode('NON_HLP'), - active: dataMode === 'NON_HLP', - }, - ], - }; - - const chartInfo = dataMode === 'HLP' ? chartDataLiquidator : chartDataNonLiquidator; - const chartData = chartInfo ? chartInfo.data : []; - const domainYRight = [ - -chartInfo?.stats.maxAbsCumulativePnl * 1.1, - chartInfo?.stats.maxAbsCumulativePnl * 1.1, - ]; - const domainYLeft = [-chartInfo?.stats.maxAbsPnl * 1.1, chartInfo?.stats.maxAbsPnl * 1.1]; - - return ( - - - - - - - - { - return Number(item.value) * -1; - }} - /> - - - {(chartData || []).map((item: any, i: number) => { - return 0 ? GREEN : RED} />; - })} - maxBarSize={20} - - - - - - ); -} diff --git a/components/home/charts/hlp-exposure.tsx b/components/home/charts/hlp.tsx similarity index 84% rename from components/home/charts/hlp-exposure.tsx rename to components/home/charts/hlp.tsx index 4aabb69..20ab5eb 100644 --- a/components/home/charts/hlp-exposure.tsx +++ b/components/home/charts/hlp.tsx @@ -14,12 +14,21 @@ import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper from '../../common/chartWrapper'; -import { BRIGHT_GREEN, CHART_HEIGHT, GREEN, RED } from '../../../constants'; import { - tooltopFormatterDate, + BLUE, + BRIGHT_GREEN, + CHART_HEIGHT, + GREEN, + ORANGE, + RED, + YAXIS_WIDTH, +} from '../../../constants'; +import { + tooltipFormatterDate, tooltipFormatterCurrency, xAxisFormatter, - yaxisFormatterNumber, + yaxisFormatter, + tooltipFormatterLongShort, } from '../../../helpers'; import { getTokenHex } from '@/constants/tokens'; import { asset_ctxs, hlp_liquidator_pnl, hlp_positions } from '@/constants/api'; @@ -27,7 +36,7 @@ const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; export default function Hlp() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('NET'); + const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('PNL'); const [coins, setCoins] = useState([]); const [dataHlpPositions, loadingDataHlpPositions, errorDataHlpPositions] = useRequest( REQUESTS[0], @@ -110,6 +119,7 @@ export default function Hlp() { let ethOraclePxPrev: number | null | undefined = null; let prevTime: string | null = null; + let hedgedCumulativePnl = 0; hlpPositions.forEach((item: HlpPosition) => { let { time, coin, daily_ntl } = item; if (!map.has(time)) { @@ -118,15 +128,17 @@ export default function Hlp() { let hedgedPnl = pnl ?? 0; let prevDayNtlPosition = prevTime ? map.get(prevTime)?.daily_ntl : null; if (ethOraclePxPrev && ethOraclePx && prevDayNtlPosition) { - const ethPxChange = 1 - ethOraclePxPrev / ethOraclePx; + const ethPxChange = 1 - ethOraclePx / ethOraclePxPrev; const ethPnL = prevDayNtlPosition * ethPxChange; hedgedPnl += ethPnL; } + hedgedCumulativePnl += hedgedPnl; map.set(time, { time: new Date(time), daily_ntl: daily_ntl, [`${coin}`]: daily_ntl, hedged_pnl: hedgedPnl, + hedged_cumulative_pnl: hedgedCumulativePnl, Other: 0, }); ethOraclePxPrev = ethOraclePx; @@ -140,7 +152,12 @@ export default function Hlp() { map.forEach((entry) => { const coinEntries = Object.entries(entry).filter( - ([key]) => key !== 'time' && key !== 'daily_ntl' && key !== 'hedged_pnl' && key !== 'other' + ([key]) => + key !== 'time' && + key !== 'daily_ntl' && + key !== 'hedged_pnl' && + key !== 'other' && + key !== 'hedged_cumulative_pnl' ); const sortedCoinEntries = coinEntries.sort( (a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1])) @@ -166,17 +183,7 @@ export default function Hlp() { const controls = { toggles: [ { - text: 'Net notional position', - event: () => setDataMode('NET'), - active: dataMode === 'NET', - }, - { - text: 'By coins', - event: () => setDataMode('COINS'), - active: dataMode === 'COINS', - }, - { - text: 'Net PnL', + text: 'PnL', event: () => setDataMode('PNL'), active: dataMode === 'PNL', }, @@ -185,6 +192,16 @@ export default function Hlp() { event: () => setDataMode('HEDGED'), active: dataMode === 'HEDGED', }, + { + text: 'Net notional position', + event: () => setDataMode('NET'), + active: dataMode === 'NET', + }, + { + text: 'Coin positions', + event: () => setDataMode('COINS'), + active: dataMode === 'COINS', + }, ], }; @@ -208,7 +225,7 @@ export default function Hlp() { }, [loading, error, hlpPnL]); return ( - + @@ -223,11 +240,11 @@ export default function Hlp() { tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} dx={6} width={75} - tickFormatter={yaxisFormatterNumber} + tickFormatter={yaxisFormatter} /> {(formattedData || []).map((item: GroupedData, i: number) => { - return 0 ? GREEN : RED} />; + return 0 ? BLUE : ORANGE} />; })} )} @@ -273,18 +290,27 @@ export default function Hlp() { ); })} {dataMode === 'HEDGED' && ( - - {(formattedData || []).map((item: GroupedData, i: number) => { - return 0 ? GREEN : RED} />; - })} - + <> + + + {(formattedData || []).map((item: GroupedData, i: number) => { + return 0 ? GREEN : RED} />; + })} + + )} {dataMode === 'PNL' && ( <> @@ -299,7 +325,7 @@ export default function Hlp() { isAnimationActive={false} type='monotone' dataKey={'pnl'} - name={'Net PnL'} + name={'PnL'} fill={'#fff'} maxBarSize={20} > diff --git a/components/home/charts/cumulative-notional-liquidated.tsx b/components/home/charts/liquidator.tsx similarity index 63% rename from components/home/charts/cumulative-notional-liquidated.tsx rename to components/home/charts/liquidator.tsx index 79f2613..be30892 100644 --- a/components/home/charts/cumulative-notional-liquidated.tsx +++ b/components/home/charts/liquidator.tsx @@ -8,6 +8,7 @@ import { ResponsiveContainer, ComposedChart, Line, + Cell, } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; @@ -19,12 +20,15 @@ import { BRIGHT_GREEN, BRAND_GREEN_2, BRAND_GREEN_3, + GREEN, + RED, } from '../../../constants'; import { tooltipLabelFormatter, yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency, + tooltipFormatterDate, } from '../../../helpers'; import { getTokenHex } from '../../../constants/tokens'; import { @@ -32,6 +36,8 @@ import { daily_notional_liquidated_total, daily_notional_liquidated_by_leverage_type, daily_notional_liquidated_by_coin, + hlp_liquidator_pnl_false, + cumulative_hlp_liquidator_pnl_false, } from '../../../constants/api'; const REQUESTS = [ @@ -39,12 +45,14 @@ const REQUESTS = [ daily_notional_liquidated_total, daily_notional_liquidated_by_leverage_type, daily_notional_liquidated_by_coin, + hlp_liquidator_pnl_false, + cumulative_hlp_liquidator_pnl_false, ]; -export default function CumulativeNotionalLiquidated() { +export default function LiquidatorChart() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'PNL'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); @@ -61,20 +69,38 @@ export default function CumulativeNotionalLiquidated() { ] = useRequest(REQUESTS[2], [], 'chart_data'); const [dataDailyLiquidatedByCoins, loadingDailyLiquidatedByCoins, errorDailyLiquidatedByCoins] = useRequest(REQUESTS[3], [], 'chart_data'); + const [dataLiquidatorPnl, loadingLiquidatorPnl, errorLiquidatorPnl] = useRequest( + REQUESTS[4], + [], + 'chart_data' + ); + const [ + dataLiquidatorCumulativePnl, + loadingLiquidatorCumulativePnl, + errorLiquidatorCumulativePnl, + ] = useRequest(REQUESTS[5], [], 'chart_data'); + const [formattedLiquidatorPnl, setFormattedLiquidatorPnl] = useState([]); - console.log('test coinKeys', coinKeys); + type LiquidatorPnl = { + time: Date; + pnl: number; + cumulativePnl: number; + }; const loading = loadingCumulativeLiquidated || loadingDailyLiquidatedTotal || loadingDailyLiquidatedByMargin || - loadingDailyLiquidatedByCoins; + loadingDailyLiquidatedByCoins || + loadingLiquidatorPnl || + loadingLiquidatorCumulativePnl; const error = errorCumulativeLiquidated || errorDailyUsdVolumeTotal || errorDailyLiquidatedByMargin || - errorDailyLiquidatedByCoins; - + errorDailyLiquidatedByCoins || + errorLiquidatorPnl || + errorLiquidatorCumulativePnl; type CumulativeLiquidationData = { cumulative: number; time: string }; const formatCumulativeLiquidatedByTime = ( @@ -87,6 +113,28 @@ export default function CumulativeNotionalLiquidated() { return result; }; + const formatLiquidatorPnl = ( + dataLiquidatorPnl: any, + dataLiquidatorCumulativePnl: any + ): LiquidatorPnl[] => { + const map = new Map(); + dataLiquidatorPnl.map((item: any) => { + let entry = { + time: new Date(item.time), + pnl: item.total_pnl, + cumulativePnl: 0, + }; + map.set(item.time, entry); + }); + + dataLiquidatorCumulativePnl.map((item: any) => { + let existingEntry = map.get(item.time)!; + existingEntry.cumulativePnl = item.cumulative_pnl; + }); + + return Array.from(map.values()); + }; + type LiquidationData = { time: string; leverage_type: 'Cross' | 'Isolated'; @@ -187,6 +235,11 @@ export default function CumulativeNotionalLiquidated() { dataDailyLiquidatedByCoins, formattedCumulativeLiquidatedByTime ); + const newFormattedLiquidatorPnl = formatLiquidatorPnl( + dataLiquidatorPnl, + dataLiquidatorCumulativePnl + ); + setFormattedLiquidatorPnl(newFormattedLiquidatorPnl); setCoinKeys(extractUniqueCoins(formattedDailyTradesByCoins)); setFormattedDataMargin(formattedVolumeByMargin); setFormattedDataCoins(formattedDailyTradesByCoins); @@ -196,15 +249,20 @@ export default function CumulativeNotionalLiquidated() { const controls = { toggles: [ { - text: 'Coins', + text: 'By coin', event: () => setDataMode('COINS'), active: dataMode === 'COINS', }, { - text: 'Cross / Isolated Margin', + text: 'By margin type', event: () => setDataMode('MARGIN'), active: dataMode === 'MARGIN', }, + { + text: 'Liquidator PnL', + event: () => setDataMode('PNL'), + active: dataMode === 'PNL', + }, ], }; @@ -214,19 +272,41 @@ export default function CumulativeNotionalLiquidated() { } }, [loading, error]); + const dataModeToData = (dataMode: string) => { + switch (dataMode) { + case 'COINS': + return formattedDataCoins; + case 'MARGIN': + return formattedDataMargin; + case 'PNL': + return formattedLiquidatorPnl; + } + }; + + const pnlYDomain = () => { + let maxPnl = formattedLiquidatorPnl.reduce((max, curr) => { + return Math.abs(max.pnl) > Math.abs(curr.pnl) ? max : curr; + }).pnl; + return [-1 * Math.abs(maxPnl) * 1.1, Math.abs(maxPnl) * 1.1]; + }; + + const cumulativePnlYDomain = () => { + let maxCumulativePnl = formattedLiquidatorPnl.reduce((max, curr) => { + return Math.abs(max.cumulativePnl) > Math.abs(curr.cumulativePnl) ? max : curr; + }).cumulativePnl; + + return [-1 * Math.abs(maxCumulativePnl) * 1.1, Math.abs(maxCumulativePnl) * 1.1]; + }; return ( - + - - )} - + {dataMode !== 'PNL' && ( + <> + + + + + )} + {dataMode === 'PNL' && ( + <> + + + + {formattedLiquidatorPnl.map((item: any, i: number) => { + return 0 ? GREEN : RED} />; + })} + + + + )} diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index de423be..8f6c60e 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -21,7 +21,7 @@ import { yaxisFormatter, xAxisFormatter, tooltipFormatterCurrency, - tooltopFormatterDate, + tooltipFormatterDate, } from '../../../helpers'; const REQUESTS = [cumulative_user_pnl, user_pnl]; @@ -128,7 +128,7 @@ export default function TradersProfitLossChart() { /> - + {((data && data.data) || []).map((item: any, i: number) => { return 0 ? GREEN : RED} />; })} - maxBarSize={20} { return ( @@ -86,7 +85,6 @@ const Main = () => { - diff --git a/constants/index.ts b/constants/index.ts index 78cb82b..0822ee4 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -3,6 +3,8 @@ export const YAXIS_WIDTH = 60; export const GREEN = '#62B143'; export const RED = '#DC0428'; +export const ORANGE = '#ffa500'; +export const BLUE = '#008ECC'; export const BRIGHT_GREEN = '#97ffe4'; export const BRAND_GREEN_2 = '#4E8174'; export const BRAND_GREEN_3 = '#72BDAB'; diff --git a/helpers/index.ts b/helpers/index.ts index daff064..49507e7 100644 --- a/helpers/index.ts +++ b/helpers/index.ts @@ -185,7 +185,16 @@ export const tooltipFormatterCurrency = (value: number | string): string => { return formatNumberWithOptions(Number(value), { currency: true, compact: true }); }; -export const tooltopFormatterDate = (label: any) => { +export const tooltipFormatterLongShort = (value: number | string): string => { + let formattedNumber = formatNumberToCompactForm(Math.abs(+value)); + if (+value < 0) { + return `Short $${formattedNumber}`; + } else { + return `Long $${formattedNumber}`; + } +}; + +export const tooltipFormatterDate = (label: any) => { const date = new Date(label); return `Date : ${date.toLocaleDateString()}`; }; From 40ab55ef7c7071b7674c869c6c8ae1e6a56a0782 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Wed, 26 Jul 2023 18:57:55 -0400 Subject: [PATCH 17/59] fixes --- components/home/charts/date-range.tsx | 2 +- components/home/charts/funding-rate.tsx | 2 +- components/home/charts/hlp.tsx | 39 ++-- components/home/charts/liquidity.tsx | 4 +- components/home/charts/unique-users-coin.tsx | 2 +- components/home/charts/volume-total.tsx | 188 +++++++++++++++++++ components/home/main/index.tsx | 26 +-- contexts/data.tsx | 2 +- 8 files changed, 231 insertions(+), 34 deletions(-) create mode 100644 components/home/charts/volume-total.tsx diff --git a/components/home/charts/date-range.tsx b/components/home/charts/date-range.tsx index eb71cea..b662943 100644 --- a/components/home/charts/date-range.tsx +++ b/components/home/charts/date-range.tsx @@ -9,7 +9,7 @@ import 'react-date-range/dist/styles.css'; import 'react-date-range/dist/theme/default.css'; const ALL_TIME_ID = 4; -const DATA_START_DATE = new Date('2023-05-10'); +const DATA_START_DATE = new Date('2023-06-14'); const DATE_NOW = new Date(); export const DateRangeSelect = () => { diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index cde5d8a..2ca2999 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -149,7 +149,7 @@ export default function FundingRate() { data={formattedData} coinSelectors={coinSelectors} > - + ('PNL'); @@ -68,6 +62,7 @@ export default function Hlp() { time: string; coin: string; avg_oracle_px: number; + first_oracle_px: number; avg_open_interest: number; }; @@ -88,7 +83,7 @@ export default function Hlp() { const map = new Map(); assetCtxs.forEach((item) => { if (item.coin === 'ETH') { - map.set(item.time, item.avg_oracle_px); + map.set(item.time, item.first_oracle_px); } }); return map; @@ -113,11 +108,21 @@ export default function Hlp() { return map; }; + function getNextTime(time: string) { + const date = new Date(time); + date.setDate(date.getDate() + 1); + + var dd = String(date.getDate()).padStart(2, '0'); + var mm = String(date.getMonth() + 1).padStart(2, '0'); //January is 0! + var yyyy = date.getFullYear(); + + return yyyy + '-' + mm + '-' + dd + 'T00:00:00'; + } + const makeFormattedData = (hlpPositions: HlpPosition[]): [GroupedData[], string[]] => { const map = new Map(); const uniqueTopCoins = new Set(); - let ethOraclePxPrev: number | null | undefined = null; let prevTime: string | null = null; let hedgedCumulativePnl = 0; hlpPositions.forEach((item: HlpPosition) => { @@ -126,10 +131,12 @@ export default function Hlp() { const pnl = hlpPnL.get(time)?.pnl; const ethOraclePx = ethOraclePxs.get(time); let hedgedPnl = pnl ?? 0; + const nextTime = getNextTime(time); + let ethOraclePxNext = ethOraclePxs.get(nextTime); let prevDayNtlPosition = prevTime ? map.get(prevTime)?.daily_ntl : null; - if (ethOraclePxPrev && ethOraclePx && prevDayNtlPosition) { - const ethPxChange = 1 - ethOraclePx / ethOraclePxPrev; - const ethPnL = prevDayNtlPosition * ethPxChange; + if (ethOraclePxNext && ethOraclePx && prevDayNtlPosition) { + const ethPxChange = 1 - ethOraclePx / ethOraclePxNext; + const ethPnL = -1 * prevDayNtlPosition * ethPxChange; hedgedPnl += ethPnL; } hedgedCumulativePnl += hedgedPnl; @@ -141,7 +148,6 @@ export default function Hlp() { hedged_cumulative_pnl: hedgedCumulativePnl, Other: 0, }); - ethOraclePxPrev = ethOraclePx; prevTime = time; } else { const existingEntry = map.get(time)!; @@ -205,6 +211,7 @@ export default function Hlp() { ], }; + console.log('***assetCtxs', assetCtxs); const formatData = () => { if (dataHlpPositions && assetCtxs && dataHlpPnL) { const newEthOraclePxs = getEthOraclePxs(assetCtxs); @@ -226,7 +233,7 @@ export default function Hlp() { return ( - + ([]); @@ -289,7 +289,7 @@ export default function CumulativeUsers() { maxHeight: '500px', }} itemSorter={(item) => { - return Number(item.value) * -1; + return Number(item.value); }} /> diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 75b05b2..4ef7e44 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -67,7 +67,7 @@ type TempGroupedTradeData = { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; -export default function CumulativeUsers() { +export default function UniqueUsers() { const [isMobile] = useMediaQuery('(max-width: 700px)'); const [formattedData, setFormattedData] = useState([]); diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx new file mode 100644 index 0000000..e2e14da --- /dev/null +++ b/components/home/charts/volume-total.tsx @@ -0,0 +1,188 @@ +import { + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ComposedChart, + Line, +} from 'recharts'; +import { useEffect, useState } from 'react'; +import { useRequest } from '@/hooks/useRequest'; +import { Box, Text, useMediaQuery } from '@chakra-ui/react'; +import ChartWrapper from '../../common/chartWrapper'; +import { BRIGHT_GREEN, CHART_HEIGHT, YAXIS_WIDTH } from '../../../constants'; +import { + yaxisFormatter, + xAxisFormatter, + tooltipFormatterCurrency, + tooltipLabelFormatter, + tooltipFormatterDate, +} from '../../../helpers'; +import { total_volume } from '../../../constants/api'; +import { getTokenHex } from '@/constants/tokens'; + +const REQUESTS = [total_volume]; + +export default function TotalVolumeChart() { + const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [formattedData, setFormattedData] = useState([]); + const [coins, setCoins] = useState([]); + const [dataTotalVolume, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); + + interface TotalVolume { + time: string; + total_volume: number; + coin: string; + } + + interface MergedData { + time: Date; + total: number; + [coin: string]: any; + cumulative: number; + Other: number; + } + + const makeFormattedData = (dataTotalVolume: TotalVolume[]): [MergedData[], string[]] => { + const map = new Map(); + const uniqueTopCoins = new Set(); + + let cumulative = 0; + dataTotalVolume.forEach((item: TotalVolume) => { + let { time, coin, total_volume } = item; + cumulative += total_volume; + if (!map.has(time)) { + map.set(time, { + time: new Date(time), + total: total_volume, + [`${coin}`]: total_volume, + cumulative: cumulative, + Other: 0, + }); + } else { + const existingEntry = map.get(time)!; + existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + total_volume; + existingEntry.total += total_volume; + existingEntry.cumulative = cumulative; + } + }); + + map.forEach((entry) => { + const coinEntries = Object.entries(entry).filter( + ([key]) => key !== 'time' && key !== 'total' && key !== 'cumulative' && key !== 'other' + ); + const sortedCoinEntries = coinEntries.sort( + (a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1])) + ); + const topCoins = sortedCoinEntries.slice(0, 10).map(([coin]) => coin); + const otherCoins = sortedCoinEntries.slice(10); + + topCoins.forEach((coin) => uniqueTopCoins.add(coin)); + + let otherTotal = 0; + otherCoins.forEach(([coin, value]) => { + otherTotal += value; + delete entry[coin]; + }); + entry.Other = otherTotal; + }); + + const result = Array.from(map.values()); + uniqueTopCoins.add('Other'); + return [result, Array.from(uniqueTopCoins)]; + }; + + const formatData = () => { + const [newFormattedData, coins] = makeFormattedData(dataTotalVolume); + setCoins(coins); + setFormattedData(newFormattedData); + }; + + useEffect(() => { + if (!loading && !error) { + formatData(); + } + }, [loading, error]); + + return ( + + + + + + + + + {coins.map((coin, i) => { + return ( + + ); + })} + + { + return Number(item.value) * -1; + }} + /> + + + + Top 10 Coins grouped daily and remaining coins grouped by Other + + + ); +} diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index 4e68fbd..756b2cd 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -10,7 +10,6 @@ import TradersProfitLossChart from '../charts/trader-profit'; import { DateRangeSelect } from '../charts/date-range'; import FundingRateChart from '../charts/funding-rate'; import CumulativeUsersChart from '../charts/cumulative-users'; -import CoinTradesByUsers from '../charts/unique-users-coin'; import CumulativeInflowChart from '../charts/cumulative-inflow'; import CumulativeNotionalLiquidatedChart from '../charts/liquidator'; import TableLargestUsers from '../tables/largest-users'; @@ -18,8 +17,9 @@ import TableUserDesposits from '../tables/user-deposits'; import TableLiquidatedNotional from '../tables/liquidated-notional-user'; import TableTradeCount from '../tables/user-trade-count'; import Liquidity from '../charts/liquidity'; -import Fees from '../charts/fees'; import HlpExposure from '../charts/hlp'; +import TotalVolumeChart from '../charts/volume-total'; +import UniqueUsers from '../charts/unique-users-coin'; const Main = () => { return ( @@ -76,27 +76,29 @@ const Main = () => { - - + + - - + + - + + - + - + + + + + - - - diff --git a/contexts/data.tsx b/contexts/data.tsx index 5f2b30d..91b74a5 100644 --- a/contexts/data.tsx +++ b/contexts/data.tsx @@ -29,7 +29,7 @@ export const DataContextProvider = (props: any) => { const initState: State = { dates: { - from: '2023-05-10', + from: '2023-06-14', to: DATE_TO, }, setDates: setDates, From 9400b5d0ee58128e11d86b256bc2026823825c0b Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Thu, 27 Jul 2023 11:54:08 -0400 Subject: [PATCH 18/59] fixes --- components/home/charts/hlp.tsx | 1 - components/home/charts/liquidity.tsx | 31 ----------------- components/home/charts/open-interest.tsx | 14 ++++++-- components/home/charts/top-stats.tsx | 43 +++++++++++++++++------- components/home/charts/volume-total.tsx | 16 +++++++-- helpers/index.ts | 15 ++++++--- 6 files changed, 65 insertions(+), 55 deletions(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index e9b43d9..e3ac3b1 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -211,7 +211,6 @@ export default function Hlp() { ], }; - console.log('***assetCtxs', assetCtxs); const formatData = () => { if (dataHlpPositions && assetCtxs && dataHlpPnL) { const newEthOraclePxs = getEthOraclePxs(assetCtxs); diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index ddd4af6..81b881c 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -31,7 +31,6 @@ export default function Liquidity() { const [formattedData1000, setFormattedData1000] = useState([]); const [formattedData3000, setFormattedData3000] = useState([]); const [formattedData10000, setFormattedData10000] = useState([]); - const [minMax, setMinMax] = useState(); const [coinKeys0, setCoinKeys0] = useState([]); const [coinKeys1000, setCoinKeys1000] = useState([]); @@ -89,8 +88,6 @@ export default function Liquidity() { const transformData = (data: InputData): OutputData => { const coinTotals = new Map(); - const minMax: { min: number; max: number } = { min: Infinity, max: -Infinity }; - // Compute overall totals for each coin for (let key in data) { data[key].forEach((record) => { @@ -168,31 +165,6 @@ export default function Liquidity() { }; }; - type MinMaxValues = { - min: number; - max: number; - }; - - const getMinMaxValues = (data: any): MinMaxValues => { - let min = Infinity; - let max = -Infinity; - for (let prop in data) { - if (Object.prototype.hasOwnProperty.call(data, prop)) { - const propData = data[prop]; - propData.forEach((item: any) => { - for (let key in item) { - if (key !== 'time' && typeof item[key] === 'number') { - const value = item[key] as number; - min = Math.min(min, value); - max = Math.max(max, value); - } - } - }); - } - } - return { min, max }; - }; - const extractUniqueCoins = ( data: | OutputData['median_slippage_1000'] @@ -220,8 +192,6 @@ export default function Liquidity() { const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000); const formattedUniqueCoinKeys3000 = extractUniqueCoins(formattedData.median_slippage_3000); const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000); - const minMaxValues = getMinMaxValues(formattedData); - setMinMax(minMaxValues); setCoinKeys0(formattedUniqueCoinKeys0); setCoinKeys1000(formattedUniqueCoinKeys1000); setCoinKeys3000(formattedUniqueCoinKeys3000); @@ -271,7 +241,6 @@ export default function Liquidity() { tickMargin={10} /> ); })} + diff --git a/components/home/charts/top-stats.tsx b/components/home/charts/top-stats.tsx index 2ee2b3c..656cc46 100644 --- a/components/home/charts/top-stats.tsx +++ b/components/home/charts/top-stats.tsx @@ -1,19 +1,19 @@ 'use strict'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Grid, Text, Spinner } from '@chakra-ui/react'; import { total_users, - total_usd_volume, total_deposits, total_withdrawals, total_notional_liquidated, + total_volume, } from '../../../constants/api'; import { useRequest } from '@/hooks/useRequest'; import { formatNumber } from '@/utils/formatting'; const REQUESTS = [ total_users, - total_usd_volume, + total_volume, total_deposits, total_withdrawals, total_notional_liquidated, @@ -34,12 +34,8 @@ const TopStats = () => { 'total_users', true ); - const [dataTotalUsdVol, loadingUsdVol, errorUsdVol] = useRequest( - REQUESTS[1], - 0, - 'total_usd_volume', - true - ); + const [dataTotalVolume, loadingVol, errorVol] = useRequest(REQUESTS[1], 0, 'chart_data', true); + const [totalVolume, setTotalVolume] = useState(0); const [dataTotalDeposits, loadingTotalDeposits, errorTotalDeposits] = useRequest( REQUESTS[2], 0, @@ -58,6 +54,27 @@ const TopStats = () => { errorTotalNotionalLiquidated, ] = useRequest(REQUESTS[4], 0, 'total_notional_liquidated', true); + interface TotalVolume { + time: string; + total_volume: number; + coin: string; + } + + const computeTotalVolume = (dataTotalVolume: TotalVolume[]) => { + let totalVolume = 0; + dataTotalVolume.forEach((volume: TotalVolume) => { + totalVolume += volume.total_volume; + }); + + setTotalVolume(totalVolume); + }; + + useEffect(() => { + if (!loadingVol && !errorVol && dataTotalVolume) { + computeTotalVolume(dataTotalVolume); + } + }, [loadingVol, errorVol]); + return ( { - {dataTotalUsdVol ? `$${formatNumber(dataTotalUsdVol, 0)}` : errorUsdVol ? 'Error' : null} + {totalVolume ? `$${formatNumber(totalVolume, 0)}` : errorVol ? 'Error' : null} - diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index e2e14da..3ce2077 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -43,6 +43,7 @@ export default function TotalVolumeChart() { total: number; [coin: string]: any; cumulative: number; + unit: string; Other: number; } @@ -61,6 +62,7 @@ export default function TotalVolumeChart() { [`${coin}`]: total_volume, cumulative: cumulative, Other: 0, + unit: '$', }); } else { const existingEntry = map.get(time)!; @@ -72,7 +74,12 @@ export default function TotalVolumeChart() { map.forEach((entry) => { const coinEntries = Object.entries(entry).filter( - ([key]) => key !== 'time' && key !== 'total' && key !== 'cumulative' && key !== 'other' + ([key]) => + key !== 'time' && + key !== 'total' && + key !== 'cumulative' && + key !== 'other' && + key !== 'unit' ); const sortedCoinEntries = coinEntries.sort( (a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1])) @@ -164,7 +171,7 @@ export default function TotalVolumeChart() { /> tooltipLabelFormatter(label, args, 'total')} contentStyle={{ textAlign: 'left', background: '#0A1F1B', @@ -181,7 +188,10 @@ export default function TotalVolumeChart() { - Top 10 Coins grouped daily and remaining coins grouped by Other + + Top 10 Coins grouped daily and remaining coins grouped by Other. Volume tracked since + introduction of fees. + ); diff --git a/helpers/index.ts b/helpers/index.ts index 49507e7..c75c93e 100644 --- a/helpers/index.ts +++ b/helpers/index.ts @@ -113,7 +113,7 @@ export const tooltipLabelFormatterPercent = (label: any, args: any): any => { return date; }; -export const tooltipLabelFormatter = (label: any, args: any): any => { +export const tooltipLabelFormatter = (label: any, args: any, key?: string): any => { const hide = args && args[0] && @@ -129,12 +129,17 @@ export const tooltipLabelFormatter = (label: any, args: any): any => { const item = args && args[0] && args[0].payload && args[0]; const dateFmtString = 'Total %d-%m-%y :'; const date = strftime(dateFmtString, label); - const all = item && item.payload.all; - if (all) { + let value; + if (key) { + value = item && item.payload[key]; + } else { + value = item && item.payload.all; + } + if (value) { if ((item && item.unit === '$') || (item.payload && item.payload.unit === '$')) { - return `${date} ${formatNumberWithOptions(all, { currency: true, compact: true })}`; + return `${date} ${formatNumberWithOptions(value, { currency: true, compact: true })}`; } - return `${date} ${formatNumberWithOptions(all, { compact: true })}`; + return `${date} ${formatNumberWithOptions(value, { compact: true })}`; } return date; }; From d4b097e89fd0b1f10b4e2122cddec59dc028fe56 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Mon, 31 Jul 2023 12:29:24 -0400 Subject: [PATCH 19/59] mobile --- components/common/chartWrapper/index.tsx | 44 +++++++++++++++--------- components/home/charts/liquidity.tsx | 25 ++------------ components/home/charts/volume-total.tsx | 3 +- 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 9e8151c..b56091e 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -10,6 +10,8 @@ import { MenuList, MenuItemOption, MenuOptionGroup, + useMediaQuery, + Grid, } from '@chakra-ui/react'; interface Toggle { @@ -31,7 +33,24 @@ const Loader = () => ( ); function ChartWrapper(props: any) { + const [isMobile] = useMediaQuery('(max-width: 700px)'); const { title, loading, controls, zIndex, coinSelectors } = props; + const controlButtons = + controls && + controls.toggles && + controls.toggles.length > 0 && + controls.toggles.map((toggle: Toggle, index: number) => { + return ( + + ); + }); return ( @@ -68,22 +87,15 @@ function ChartWrapper(props: any) { mt={controls && controls.toggles && controls.toggles.length && '2'} mb='1rem' > - - {controls.toggles && - controls.toggles.length > 0 && - controls.toggles.map((toggle: Toggle, index: number) => { - return ( - - ); - })} - + {isMobile ? ( + + {controlButtons} + + ) : ( + + {controlButtons} + + )} )} {coinSelectors && ( diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 81b881c..57daabe 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -26,7 +26,6 @@ const REQUESTS = [liquidity_by_coin]; export default function Liquidity() { const [isMobile] = useMediaQuery('(max-width: 700px)'); - const [formattedData0, setFormattedData0] = useState([]); const [formattedData1000, setFormattedData1000] = useState([]); const [formattedData3000, setFormattedData3000] = useState([]); @@ -38,6 +37,7 @@ export default function Liquidity() { const [coinKeys10000, setCoinKeys10000] = useState([]); const [dataMode, setDataMode] = useState<'0' | '1000' | '3000' | '10000'>('0'); + const [coinsSelected, setCoinsSelected] = useState(['ETH', 'BTC', 'ARB']); const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); const loading = loadingLiqudity; @@ -86,30 +86,9 @@ export default function Liquidity() { }; const transformData = (data: InputData): OutputData => { - const coinTotals = new Map(); - - // Compute overall totals for each coin - for (let key in data) { - data[key].forEach((record) => { - coinTotals.set( - key, - (coinTotals.get(key) || 0) + - record.median_slippage_1000 + - record.median_slippage_3000 + - record.median_slippage_10000 - ); - }); - } - - // Get the top 10 coins by total over the whole time period - const topCoins = Array.from(coinTotals.entries()) - .sort((a, b) => b[1] - a[1]) - .slice(0, 10) - .map(([coin]) => coin); - // Filter data for each category by top 10 coins const filteredData: InputData = {}; - for (let coin of topCoins) { + for (let coin of coinsSelected) { filteredData[coin] = data[coin]; } diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index 3ce2077..ad286c2 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -19,7 +19,6 @@ import { xAxisFormatter, tooltipFormatterCurrency, tooltipLabelFormatter, - tooltipFormatterDate, } from '../../../helpers'; import { total_volume } from '../../../constants/api'; import { getTokenHex } from '@/constants/tokens'; @@ -116,7 +115,7 @@ export default function TotalVolumeChart() { return ( - + Date: Mon, 31 Jul 2023 12:31:38 -0400 Subject: [PATCH 20/59] fix --- components/common/chartWrapper/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index b56091e..2302d16 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -92,7 +92,7 @@ function ChartWrapper(props: any) { {controlButtons} ) : ( - + {controlButtons} )} From 120d966531646197067372638f6d04bce83485e1 Mon Sep 17 00:00:00 2001 From: chameleon Date: Mon, 31 Jul 2023 15:48:47 -0400 Subject: [PATCH 21/59] Updating hedged pnl --- components/home/charts/hlp.tsx | 58 +++++++++++++++++++--------------- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index e3ac3b1..edefe35 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -43,7 +43,7 @@ export default function Hlp() { [], 'chart_data' ); - const [ethOraclePxs, setEthOraclePxs] = useState>(new Map()); + const [oraclePxs, setOraclePxs] = useState>(new Map()); const [hlpPnL, setHlpPnL] = useState>(new Map()); const [formattedHlpPnL, setFormattedHlpPnL] = useState([]); const [formattedData, setFormattedData] = useState([]); @@ -79,12 +79,10 @@ export default function Hlp() { hedged_pnl: number; }; - const getEthOraclePxs = (assetCtxs: AssetCtx[]): Map => { + const getOraclePxs = (assetCtxs: AssetCtx[]): Map => { const map = new Map(); assetCtxs.forEach((item) => { - if (item.coin === 'ETH') { - map.set(item.time, item.first_oracle_px); - } + map.set(item.coin + item.time, item.first_oracle_px); }); return map; }; @@ -125,35 +123,43 @@ export default function Hlp() { let prevTime: string | null = null; let hedgedCumulativePnl = 0; + hlpPositions.forEach((item: HlpPosition) => { let { time, coin, daily_ntl } = item; if (!map.has(time)) { - const pnl = hlpPnL.get(time)?.pnl; - const ethOraclePx = ethOraclePxs.get(time); - let hedgedPnl = pnl ?? 0; - const nextTime = getNextTime(time); - let ethOraclePxNext = ethOraclePxs.get(nextTime); - let prevDayNtlPosition = prevTime ? map.get(prevTime)?.daily_ntl : null; - if (ethOraclePxNext && ethOraclePx && prevDayNtlPosition) { - const ethPxChange = 1 - ethOraclePx / ethOraclePxNext; - const ethPnL = -1 * prevDayNtlPosition * ethPxChange; - hedgedPnl += ethPnL; - } - hedgedCumulativePnl += hedgedPnl; + const pnl = hlpPnL.get(time)?.pnl || 0; + hedgedCumulativePnl += pnl; map.set(time, { time: new Date(time), - daily_ntl: daily_ntl, - [`${coin}`]: daily_ntl, - hedged_pnl: hedgedPnl, + daily_ntl: 0, + hedged_pnl: pnl, hedged_cumulative_pnl: hedgedCumulativePnl, Other: 0, }); prevTime = time; - } else { - const existingEntry = map.get(time)!; - existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + daily_ntl; - existingEntry.daily_ntl += daily_ntl; } + + const existingEntry = map.get(time)!; + existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + daily_ntl; + existingEntry.daily_ntl += daily_ntl; + + const oraclePx = oraclePxs.get(coin + time); + let hedgedPnl = 0; + const nextTime = getNextTime(time); + let oraclePxNext = oraclePxs.get(coin + nextTime); + + let prevTimeData = prevTime ? map.get(prevTime) : null; + let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; + + if (oraclePxNext && oraclePx && prevDayNtlPosition) { + const pxChange = 1 - oraclePx / oraclePxNext; + const pnl = -1 * prevDayNtlPosition * pxChange; + hedgedPnl += pnl; + } + + existingEntry.hedged_pnl += hedgedPnl; + hedgedCumulativePnl += hedgedPnl; + existingEntry.hedged_cumulative_pnl = hedgedCumulativePnl; }); map.forEach((entry) => { @@ -213,8 +219,8 @@ export default function Hlp() { const formatData = () => { if (dataHlpPositions && assetCtxs && dataHlpPnL) { - const newEthOraclePxs = getEthOraclePxs(assetCtxs); - setEthOraclePxs(newEthOraclePxs); + const newOraclePxs = getOraclePxs(assetCtxs); + setOraclePxs(newOraclePxs); const newHlpPnL = makeHlpPnl(dataHlpPnL); setFormattedHlpPnL(Array.from(newHlpPnL.values())); setHlpPnL(newHlpPnL); diff --git a/package-lock.json b/package-lock.json index 5c5b798..ed29c88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "framer-motion": "^10.12.16", "lodash": "^4.17.21", "moment": "^2.29.4", - "next": "13.4.4", + "next": "^13.4.4", "polished": "^4.2.2", "prettier": "^3.0.0", "react": "18.2.0", diff --git a/package.json b/package.json index 424ae95..e056236 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "framer-motion": "^10.12.16", "lodash": "^4.17.21", "moment": "^2.29.4", - "next": "13.4.4", + "next": "^13.4.4", "polished": "^4.2.2", "prettier": "^3.0.0", "react": "18.2.0", From fccc727079d7462e37966c5d175fad63d7b06f1e Mon Sep 17 00:00:00 2001 From: chameleon Date: Mon, 31 Jul 2023 16:05:54 -0400 Subject: [PATCH 22/59] add explainer --- components/home/charts/hlp.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index edefe35..149a353 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -143,6 +143,7 @@ export default function Hlp() { existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + daily_ntl; existingEntry.daily_ntl += daily_ntl; + // hedge the previous day's position and add the pnl from that to the current day const oraclePx = oraclePxs.get(coin + time); let hedgedPnl = 0; const nextTime = getNextTime(time); @@ -152,8 +153,10 @@ export default function Hlp() { let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; if (oraclePxNext && oraclePx && prevDayNtlPosition) { + // calculate price movement const pxChange = 1 - oraclePx / oraclePxNext; const pnl = -1 * prevDayNtlPosition * pxChange; + // update hedged pnl based on the price movement and previous days position hedgedPnl += pnl; } From 9bf33b18e370de254e9dbc9891a7a5c7a47fdf5b Mon Sep 17 00:00:00 2001 From: chameleon Date: Mon, 31 Jul 2023 16:47:22 -0400 Subject: [PATCH 23/59] Changing slippage graph --- components/home/charts/hlp.tsx | 20 ++++++++++-- components/home/charts/liquidity.tsx | 48 +++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 149a353..69699bf 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -143,7 +143,6 @@ export default function Hlp() { existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + daily_ntl; existingEntry.daily_ntl += daily_ntl; - // hedge the previous day's position and add the pnl from that to the current day const oraclePx = oraclePxs.get(coin + time); let hedgedPnl = 0; const nextTime = getNextTime(time); @@ -153,10 +152,8 @@ export default function Hlp() { let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; if (oraclePxNext && oraclePx && prevDayNtlPosition) { - // calculate price movement const pxChange = 1 - oraclePx / oraclePxNext; const pnl = -1 * prevDayNtlPosition * pxChange; - // update hedged pnl based on the price movement and previous days position hedgedPnl += pnl; } @@ -358,6 +355,23 @@ export default function Hlp() { Top 10 Coins grouped daily and remaining coins grouped by Other )} + + {dataMode === 'PNL' && ( + PNL over time + )} + + + + {dataMode === 'HEDGED' && ( + Hedged PNL over time + )} + + + + {dataMode === 'NET' && ( + Net notional position over time + )} + ); } diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 57daabe..0bc7e92 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -11,7 +11,7 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT } from '../../../constants'; import { tooltipFormatter, @@ -31,6 +31,7 @@ export default function Liquidity() { const [formattedData3000, setFormattedData3000] = useState([]); const [formattedData10000, setFormattedData10000] = useState([]); + const [coinKeys, setCoinKeys] = useState([]); const [coinKeys0, setCoinKeys0] = useState([]); const [coinKeys1000, setCoinKeys1000] = useState([]); const [coinKeys3000, setCoinKeys3000] = useState([]); @@ -85,6 +86,14 @@ export default function Liquidity() { median_slippage_10000: { time: Date; [key: string]: number | Date | string }[]; }; + const extractCoins = (data: InputData): string[] => { + let coins = []; + for (let coin of Object.keys(data)) { + coins.push(coin); + } + return coins; + } + const transformData = (data: InputData): OutputData => { // Filter data for each category by top 10 coins const filteredData: InputData = {}; @@ -148,7 +157,7 @@ export default function Liquidity() { data: | OutputData['median_slippage_1000'] | OutputData['median_slippage_10000'] - | OutputData['median_slippage_1000'] + | OutputData['median_slippage_3000'] ): string[] => { const coinSet = new Set(); data.forEach((record) => { @@ -162,6 +171,8 @@ export default function Liquidity() { }; const formatData = () => { + const extractedCoinKeys = extractCoins(dataLiqudity); + setCoinKeys(extractedCoinKeys); const formattedData = transformData(dataLiqudity); setFormattedData0(formattedData.median_slippage_0); setFormattedData1000(formattedData.median_slippage_1000); @@ -171,6 +182,7 @@ export default function Liquidity() { const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000); const formattedUniqueCoinKeys3000 = extractUniqueCoins(formattedData.median_slippage_3000); const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000); + setCoinKeys0(formattedUniqueCoinKeys0); setCoinKeys1000(formattedUniqueCoinKeys1000); setCoinKeys3000(formattedUniqueCoinKeys3000); @@ -201,6 +213,34 @@ export default function Liquidity() { ? coinKeys3000 : coinKeys10000; + const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { + if (a.isChecked !== b.isChecked) { + return a.isChecked ? -1 : 1; + } + return a.name.localeCompare(b.name); + }; + + const coinSelectors = coinKeys + .map((coinKey: string) => { + return { + name: coinKey, + event: () => + setCoinsSelected((coinsSelected) => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(); + return newCoinsSelected; + }), + isChecked: coinsSelected.includes(coinKey), + }; + }) + .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); return ( @@ -256,9 +297,6 @@ export default function Liquidity() { })} - - Top 10 Coins over time - ); } From e07cfd24e0069b2015fe0c1fd525266e3b753d69 Mon Sep 17 00:00:00 2001 From: chameleon Date: Mon, 31 Jul 2023 16:59:26 -0400 Subject: [PATCH 24/59] Updating Hedged HLP description --- components/home/charts/hlp.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 69699bf..6682717 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -363,7 +363,7 @@ export default function Hlp() { {dataMode === 'HEDGED' && ( - Hedged PNL over time + Hedged PNL over time. Hedge the previous day's position and add to today's PNL. )} From a3da42517ca34eecfe74fd5ce01352617b0b92f7 Mon Sep 17 00:00:00 2001 From: chameleon Date: Mon, 31 Jul 2023 17:44:20 -0400 Subject: [PATCH 25/59] Fix syncing --- components/home/charts/retail-volume.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 75959dd..d2965dc 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -259,7 +259,7 @@ export default function RetailVolumeChart() { Date: Mon, 31 Jul 2023 17:47:12 -0400 Subject: [PATCH 26/59] update sync naming --- components/home/charts/liquidator.tsx | 2 +- components/home/charts/retail-volume.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index be30892..d6a88aa 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -306,7 +306,7 @@ export default function LiquidatorChart() { zIndex={7} > - + Date: Tue, 1 Aug 2023 14:15:00 -0400 Subject: [PATCH 27/59] Fix isMobile bug --- components/common/chartWrapper/index.tsx | 5 ++++- components/home/charts/cumulative-inflow.tsx | 5 +++-- components/home/charts/cumulative-users.tsx | 5 +++-- components/home/charts/fees.tsx | 6 ++++-- components/home/charts/funding-rate.tsx | 4 +++- components/home/charts/hlp.tsx | 7 +++++-- components/home/charts/liquidator.tsx | 4 +++- components/home/charts/liquidity.tsx | 5 ++++- components/home/charts/open-interest.tsx | 5 +++-- components/home/charts/retail-volume.tsx | 5 ++++- components/home/charts/trader-profit.tsx | 5 +++-- components/home/charts/unique-users-coin.tsx | 5 ++++- components/home/charts/volume-num-trades.tsx | 5 ++++- components/home/charts/volume-total.tsx | 6 ++++-- hooks/isMobile.ts | 11 +++++++++++ 15 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 hooks/isMobile.ts diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 2302d16..432be8b 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -32,8 +32,11 @@ const Loader = () => ( ); + + + function ChartWrapper(props: any) { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + let isMobile = props.isMobile; const { title, loading, controls, zIndex, coinSelectors } = props; const controlButtons = controls && diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 2acf73d..15ed7db 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -22,11 +22,12 @@ import { tooltipFormatterDate, } from '../../../helpers'; import { daily_inflow, cumulative_inflow } from '../../../constants/api'; +import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [daily_inflow, cumulative_inflow]; export default function CumulativeInflow() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [formattedData, setFormattedData] = useState([]); const [dataDailyInflow, loadingDailyInflow, errorDailyInflow] = useRequest( @@ -99,7 +100,7 @@ export default function CumulativeInflow() { }, [loading, errorDailyInflow]); return ( - + diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index f09b5e0..8313f8a 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -11,6 +11,7 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; @@ -29,7 +30,7 @@ import { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; export default function CumulativeUsers() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [formattedData, setFormattedData] = useState([]); @@ -74,7 +75,7 @@ export default function CumulativeUsers() { }, [loading, error]); return ( - + diff --git a/components/home/charts/fees.tsx b/components/home/charts/fees.tsx index c56e652..a893645 100644 --- a/components/home/charts/fees.tsx +++ b/components/home/charts/fees.tsx @@ -11,6 +11,7 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; @@ -25,7 +26,8 @@ import { total_accrued_fees } from '../../../constants/api'; const REQUESTS = [total_accrued_fees]; export default function Fees() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [formattedData, setFormattedData] = useState([]); const [dailyFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -68,7 +70,7 @@ export default function Fees() { }, [loading, error]); return ( - + diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 2ca2999..efa1416 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -11,6 +11,7 @@ import { import { useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT } from '../../../constants'; import { @@ -25,7 +26,7 @@ import { funding_rate } from '../../../constants/api'; const REQUESTS = [funding_rate]; export default function FundingRate() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [coinKeys, setCoinKeys] = useState([]); const [formattedData, setFormattedData] = useState([]); @@ -148,6 +149,7 @@ export default function FundingRate() { loading={loading} data={formattedData} coinSelectors={coinSelectors} + isMobile={isMobile} > diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 6682717..745cd6f 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -13,6 +13,8 @@ import { import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; + import ChartWrapper from '../../common/chartWrapper'; import { BLUE, BRIGHT_GREEN, CHART_HEIGHT, GREEN, ORANGE, RED } from '../../../constants'; import { @@ -29,7 +31,8 @@ const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; const DAY = 60 * 60 * 24 * 1000; export default function Hlp() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('PNL'); const [coins, setCoins] = useState([]); const [dataHlpPositions, loadingDataHlpPositions, errorDataHlpPositions] = useRequest( @@ -237,7 +240,7 @@ export default function Hlp() { }, [loading, error, hlpPnL]); return ( - + diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index d6a88aa..c7ba7f6 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -12,6 +12,7 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { @@ -50,7 +51,7 @@ const REQUESTS = [ ]; export default function LiquidatorChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'PNL'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); @@ -304,6 +305,7 @@ export default function LiquidatorChart() { data={dataModeToData(dataMode)} controls={controls} zIndex={7} + isMobile={isMobile} > diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 0bc7e92..5ac2881 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -12,6 +12,7 @@ import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import { useIsMobile } from '@/hooks/isMobile'; import { CHART_HEIGHT } from '../../../constants'; import { tooltipFormatter, @@ -25,7 +26,8 @@ import { liquidity_by_coin } from '../../../constants/api'; const REQUESTS = [liquidity_by_coin]; export default function Liquidity() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [formattedData0, setFormattedData0] = useState([]); const [formattedData1000, setFormattedData1000] = useState([]); const [formattedData3000, setFormattedData3000] = useState([]); @@ -249,6 +251,7 @@ export default function Liquidity() { controls={controls} zIndex={8} coinSelectors={coinSelectors} + isMobile={isMobile} > diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index 585ad34..77a9233 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -22,11 +22,12 @@ import { } from '../../../helpers'; import { getTokenHex } from '../../../constants/tokens'; import { open_interest } from '../../../constants/api'; +import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [open_interest]; export default function VolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [coinKeys, setCoinKeys] = useState([]); const [formattedData, setFormattedData] = useState([]); @@ -125,7 +126,7 @@ export default function VolumeChart() { }, [loading]); return ( - + diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index ce0dc3e..14e46f9 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -34,6 +34,7 @@ import { daily_usd_volume_by_crossed, daily_usd_volume_by_user, } from '../../../constants/api'; +import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [ cumulative_usd_volume, @@ -44,7 +45,8 @@ const REQUESTS = [ ]; export default function RetailVolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); @@ -255,6 +257,7 @@ export default function RetailVolumeChart() { data={dataMode === 'COINS' ? formattedDataCoins : formattedDataMargin} zIndex={9} controls={controls} + isMobile={isMobile} > (null); const [dataCumulativeUserPNL, loadingCumulativeUserPNL, errorCumulativeUserPNL] = useRequest( @@ -101,7 +102,7 @@ export default function TradersProfitLossChart() { }, [loading, error]); return ( - + diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 4ef7e44..1dbb98d 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -12,6 +12,8 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; + import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; import { @@ -68,7 +70,7 @@ type TempGroupedTradeData = { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; export default function UniqueUsers() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [formattedData, setFormattedData] = useState([]); const [coinKeys, setCoinKeys] = useState([]); @@ -196,6 +198,7 @@ export default function UniqueUsers() { loading={loading} data={formattedData} zIndex={8} + isMobile={isMobile} > diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index 3f76a84..81f08b2 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -13,6 +13,8 @@ import { import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; + import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, @@ -45,7 +47,7 @@ const REQUESTS = [ ]; export default function VolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'USER'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); @@ -254,6 +256,7 @@ export default function VolumeChart() { } controls={controls} zIndex={9} + isMobile={isMobile} > ([]); const [coins, setCoins] = useState([]); const [dataTotalVolume, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -114,7 +116,7 @@ export default function TotalVolumeChart() { }, [loading, error]); return ( - + diff --git a/hooks/isMobile.ts b/hooks/isMobile.ts new file mode 100644 index 0000000..3bac769 --- /dev/null +++ b/hooks/isMobile.ts @@ -0,0 +1,11 @@ +import { useEffect, useState } from 'react'; + + +export function useIsMobile() { + let [isMobile, setIsMobile] = useState(window.innerWidth < 700); + useEffect(() => { + setIsMobile(window.innerWidth < 700); + }, [window.innerWidth]); + + return [isMobile]; +} \ No newline at end of file From 461a5776d68faafa446c835be1e6c431a90b3c1b Mon Sep 17 00:00:00 2001 From: chameleon Date: Tue, 1 Aug 2023 14:36:37 -0400 Subject: [PATCH 28/59] style --- components/common/chartWrapper/index.tsx | 7 +--- components/home/charts/cumulative-inflow.tsx | 2 +- components/home/charts/cumulative-users.tsx | 7 +++- components/home/charts/hlp.tsx | 38 +++++++++++--------- components/home/charts/liquidator.tsx | 2 +- components/home/charts/liquidity.tsx | 14 ++++---- components/home/charts/trader-profit.tsx | 7 +++- hooks/isMobile.ts | 13 ++++--- 8 files changed, 49 insertions(+), 41 deletions(-) diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 432be8b..496a9f3 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -32,9 +32,6 @@ const Loader = () => ( ); - - - function ChartWrapper(props: any) { let isMobile = props.isMobile; const { title, loading, controls, zIndex, coinSelectors } = props; @@ -95,9 +92,7 @@ function ChartWrapper(props: any) { {controlButtons} ) : ( - - {controlButtons} - + {controlButtons} )} )} diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 15ed7db..d4fe2e2 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -27,7 +27,7 @@ import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [daily_inflow, cumulative_inflow]; export default function CumulativeInflow() { - const [isMobile] = useIsMobile(); + const [isMobile] = useIsMobile(); const [formattedData, setFormattedData] = useState([]); const [dataDailyInflow, loadingDailyInflow, errorDailyInflow] = useRequest( diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index 8313f8a..3003ffe 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -75,7 +75,12 @@ export default function CumulativeUsers() { }, [loading, error]); return ( - + diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 745cd6f..ad80671 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -31,7 +31,7 @@ const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; const DAY = 60 * 60 * 24 * 1000; export default function Hlp() { - const [isMobile] = useIsMobile(); + const [isMobile] = useIsMobile(); const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('PNL'); const [coins, setCoins] = useState([]); @@ -85,7 +85,7 @@ export default function Hlp() { const getOraclePxs = (assetCtxs: AssetCtx[]): Map => { const map = new Map(); assetCtxs.forEach((item) => { - map.set(item.coin + item.time, item.first_oracle_px); + map.set(item.coin + item.time, item.first_oracle_px); }); return map; }; @@ -131,7 +131,7 @@ export default function Hlp() { let { time, coin, daily_ntl } = item; if (!map.has(time)) { const pnl = hlpPnL.get(time)?.pnl || 0; - hedgedCumulativePnl += pnl; + hedgedCumulativePnl += pnl; map.set(time, { time: new Date(time), daily_ntl: 0, @@ -147,12 +147,12 @@ export default function Hlp() { existingEntry.daily_ntl += daily_ntl; const oraclePx = oraclePxs.get(coin + time); - let hedgedPnl = 0; + let hedgedPnl = 0; const nextTime = getNextTime(time); let oraclePxNext = oraclePxs.get(coin + nextTime); - - let prevTimeData = prevTime ? map.get(prevTime) : null; - let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; + + let prevTimeData = prevTime ? map.get(prevTime) : null; + let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; if (oraclePxNext && oraclePx && prevDayNtlPosition) { const pxChange = 1 - oraclePx / oraclePxNext; @@ -160,9 +160,9 @@ export default function Hlp() { hedgedPnl += pnl; } - existingEntry.hedged_pnl += hedgedPnl; + existingEntry.hedged_pnl += hedgedPnl; hedgedCumulativePnl += hedgedPnl; - existingEntry.hedged_cumulative_pnl = hedgedCumulativePnl; + existingEntry.hedged_cumulative_pnl = hedgedCumulativePnl; }); map.forEach((entry) => { @@ -240,7 +240,13 @@ export default function Hlp() { }, [loading, error, hlpPnL]); return ( - + @@ -359,21 +365,19 @@ export default function Hlp() { )} - {dataMode === 'PNL' && ( - PNL over time - )} + {dataMode === 'PNL' && PNL over time} {dataMode === 'HEDGED' && ( - Hedged PNL over time. Hedge the previous day's position and add to today's PNL. + + Hedged PNL over time. Hedge the previous day's position and add to today's PNL. + )} - {dataMode === 'NET' && ( - Net notional position over time - )} + {dataMode === 'NET' && Net notional position over time} ); diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index c7ba7f6..b608c97 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -51,7 +51,7 @@ const REQUESTS = [ ]; export default function LiquidatorChart() { - const [isMobile] = useIsMobile(); + const [isMobile] = useIsMobile(); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'PNL'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 5ac2881..a881535 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -89,12 +89,12 @@ export default function Liquidity() { }; const extractCoins = (data: InputData): string[] => { - let coins = []; + let coins = []; for (let coin of Object.keys(data)) { - coins.push(coin); + coins.push(coin); } - return coins; - } + return coins; + }; const transformData = (data: InputData): OutputData => { // Filter data for each category by top 10 coins @@ -173,8 +173,8 @@ export default function Liquidity() { }; const formatData = () => { - const extractedCoinKeys = extractCoins(dataLiqudity); - setCoinKeys(extractedCoinKeys); + const extractedCoinKeys = extractCoins(dataLiqudity); + setCoinKeys(extractedCoinKeys); const formattedData = transformData(dataLiqudity); setFormattedData0(formattedData.median_slippage_0); setFormattedData1000(formattedData.median_slippage_1000); @@ -184,7 +184,7 @@ export default function Liquidity() { const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000); const formattedUniqueCoinKeys3000 = extractUniqueCoins(formattedData.median_slippage_3000); const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000); - + setCoinKeys0(formattedUniqueCoinKeys0); setCoinKeys1000(formattedUniqueCoinKeys1000); setCoinKeys3000(formattedUniqueCoinKeys3000); diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index 353eb9e..11d6687 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -102,7 +102,12 @@ export default function TradersProfitLossChart() { }, [loading, error]); return ( - + diff --git a/hooks/isMobile.ts b/hooks/isMobile.ts index 3bac769..46dbeee 100644 --- a/hooks/isMobile.ts +++ b/hooks/isMobile.ts @@ -1,11 +1,10 @@ import { useEffect, useState } from 'react'; - export function useIsMobile() { - let [isMobile, setIsMobile] = useState(window.innerWidth < 700); - useEffect(() => { - setIsMobile(window.innerWidth < 700); - }, [window.innerWidth]); + let [isMobile, setIsMobile] = useState(window.innerWidth < 700); + useEffect(() => { + setIsMobile(window.innerWidth < 700); + }, [window.innerWidth]); - return [isMobile]; -} \ No newline at end of file + return [isMobile]; +} From 8c7507fe7f8f4f363f6a21fc0866f86a4be5ecfd Mon Sep 17 00:00:00 2001 From: S P Date: Wed, 2 Aug 2023 15:43:35 -0400 Subject: [PATCH 29/59] Shwe/colors (#10) * updated bar charts and colors * Making changes to reflect QA * make code a bit cleaner * put coin selector sort in a util file * remove repetitive code --------- Co-authored-by: chameleon --- components/home/charts/funding-rate.tsx | 37 ++---------- components/home/charts/hlp.tsx | 5 +- components/home/charts/liquidator.tsx | 60 ++++++++++---------- components/home/charts/liquidity.tsx | 36 ++---------- components/home/charts/open-interest.tsx | 10 ++-- components/home/charts/retail-volume.tsx | 52 ++++++++--------- components/home/charts/unique-users-coin.tsx | 59 ++++++++----------- components/home/charts/volume-num-trades.tsx | 52 ++++++++--------- components/home/charts/volume-total.tsx | 42 +++++++------- constants/tokens.ts | 53 ++++++----------- helpers/utils.ts | 52 +++++++++++++++++ hooks/isMobile.ts | 3 + package-lock.json | 22 +++++++ package.json | 2 + 14 files changed, 237 insertions(+), 248 deletions(-) create mode 100644 helpers/utils.ts diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index efa1416..190f37b 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -20,7 +20,9 @@ import { formatterPercent, tooltipFormatterDate, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectors } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; import { funding_rate } from '../../../constants/api'; const REQUESTS = [funding_rate]; @@ -35,7 +37,7 @@ export default function FundingRate() { [], 'chart_data' ); - const [coinsSelected, setCoinsSelected] = useState(['ETH', 'BTC', 'ARB']); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const loading = loadingFundingRate; const error = errorFundingRate; @@ -114,34 +116,7 @@ export default function FundingRate() { } }, [loading, coinsSelected]); - const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { - if (a.isChecked !== b.isChecked) { - return a.isChecked ? -1 : 1; - } - return a.name.localeCompare(b.name); - }; - - const coinSelectors = coinKeys - .map((coinKey: string) => { - return { - name: coinKey, - event: () => - setCoinsSelected((coinsSelected) => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(); - return newCoinsSelected; - }), - isChecked: coinsSelected.includes(coinKey), - }; - }) - .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); + const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData) return ( ); diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index ad80671..d58eb8b 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -24,7 +24,8 @@ import { yaxisFormatter, tooltipFormatterLongShort, } from '../../../helpers'; -import { getTokenHex } from '@/constants/tokens'; + +import { getTokenColor } from '@/constants/tokens'; import { asset_ctxs, hlp_liquidator_pnl, hlp_positions } from '@/constants/api'; const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; @@ -304,7 +305,7 @@ export default function Hlp() { dataKey={coin} stackId='a' name={coin.toString()} - fill={getTokenHex(coin.toString())} + fill={getTokenColor(coin.toString())} key={i} maxBarSize={20} /> diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index b608c97..b34f45a 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -14,7 +14,7 @@ import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; import { useIsMobile } from '@/hooks/isMobile'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -31,7 +31,9 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { cumulative_liquidated_notional, daily_notional_liquidated_total, @@ -54,6 +56,7 @@ export default function LiquidatorChart() { const [isMobile] = useIsMobile(); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'PNL'>('COINS'); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); @@ -169,6 +172,7 @@ export default function LiquidatorChart() { type FormattedCoinTradesData = any[]; const formatDailyTradesByCoins = ( + CoinsSelected: string[], dataDailyTradesByCoin: { time: string; coin: string; daily_notional_liquidated: number }[], formattedCumulativeByTime: { [key: string]: number } ): FormattedCoinTradesData[] => { @@ -181,25 +185,21 @@ export default function LiquidatorChart() { temp[data.time].all += data.daily_notional_liquidated; } - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj).sort( - ([, aVolume], [, bVolume]) => bVolume - aVolume - ); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); - + const selectedCoinData = (obj: { [coin: string]: number }) => { + const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); + const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(top10Entries), + ...Object.fromEntries(selectedEntries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); + const selectedVolumes = selectedCoinData(volumes); return { time: new Date(time), - ...top10Volumes, + ...selectedVolumes, cumulative: formattedCumulativeByTime[time as any], unit: '', }; @@ -207,25 +207,17 @@ export default function LiquidatorChart() { return result; }; - const extractUniqueCoins = (formattedData: any[]): string[] => { + const extractUniqueCoins = (coinData: any[]): string[] => { const coinSet = new Set(); - for (const data of formattedData) { - Object.keys(data).forEach((coin) => { - if (coin !== 'time' && coin !== 'unit' && coin !== 'cumulative' && coin !== 'all') { - coinSet.add(coin); + for (const data of coinData) { + if (data.coin !== 'time' && data.coin !== 'unit' && data.coin !== 'cumulative' && data.coin !== 'all') { + coinSet.add(data.coin); } - }); - } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); } - return coinsArray; + return Array.from(coinSet); }; - const formatData = () => { + const formatData = (CoinsSelected: string[]) => { const formattedCumulativeLiquidatedByTime = formatCumulativeLiquidatedByTime(dataCumulativeLiquidated); const formattedVolumeByMargin = formatLiquidatedByMargin( @@ -233,6 +225,7 @@ export default function LiquidatorChart() { formattedCumulativeLiquidatedByTime ); const formattedDailyTradesByCoins = formatDailyTradesByCoins( + CoinsSelected, dataDailyLiquidatedByCoins, formattedCumulativeLiquidatedByTime ); @@ -241,7 +234,7 @@ export default function LiquidatorChart() { dataLiquidatorCumulativePnl ); setFormattedLiquidatorPnl(newFormattedLiquidatorPnl); - setCoinKeys(extractUniqueCoins(formattedDailyTradesByCoins)); + setCoinKeys(extractUniqueCoins(dataDailyLiquidatedByCoins)); setFormattedDataMargin(formattedVolumeByMargin); setFormattedDataCoins(formattedDailyTradesByCoins); console.log('dev formattedDailyTradesByCoins', formattedDailyTradesByCoins); @@ -269,7 +262,7 @@ export default function LiquidatorChart() { useEffect(() => { if (!loading && !error) { - formatData(); + formatData(coinsSelected); } }, [loading, error]); @@ -298,6 +291,10 @@ export default function LiquidatorChart() { return [-1 * Math.abs(maxCumulativePnl) * 1.1, Math.abs(maxCumulativePnl) * 1.1]; }; + + const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData) + + return ( @@ -336,8 +334,8 @@ export default function LiquidatorChart() { {dataMode === 'COINS' && ( <> - {coinKeys && - coinKeys.map((coinName, i) => { + { + coinsSelected.map((coinName, i) => { return ( diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index a881535..128d18e 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -20,7 +20,9 @@ import { xAxisFormatter, formatterPercent, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectors } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; import { liquidity_by_coin } from '../../../constants/api'; const REQUESTS = [liquidity_by_coin]; @@ -40,7 +42,7 @@ export default function Liquidity() { const [coinKeys10000, setCoinKeys10000] = useState([]); const [dataMode, setDataMode] = useState<'0' | '1000' | '3000' | '10000'>('0'); - const [coinsSelected, setCoinsSelected] = useState(['ETH', 'BTC', 'ARB']); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); const loading = loadingLiqudity; @@ -215,34 +217,8 @@ export default function Liquidity() { ? coinKeys3000 : coinKeys10000; - const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { - if (a.isChecked !== b.isChecked) { - return a.isChecked ? -1 : 1; - } - return a.name.localeCompare(b.name); - }; + const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); - const coinSelectors = coinKeys - .map((coinKey: string) => { - return { - name: coinKey, - event: () => - setCoinsSelected((coinsSelected) => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(); - return newCoinsSelected; - }), - isChecked: coinsSelected.includes(coinKey), - }; - }) - .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); return ( diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index 77a9233..f00bd61 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -11,7 +11,7 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { BRIGHT_GREEN, CHART_HEIGHT, GREEN, YAXIS_WIDTH } from '../../../constants'; import { xAxisFormatter, @@ -20,7 +20,8 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; + +import { getTokenColor } from '../../../constants/tokens'; import { open_interest } from '../../../constants/api'; import { useIsMobile } from '@/hooks/isMobile'; @@ -28,8 +29,9 @@ const REQUESTS = [open_interest]; export default function VolumeChart() { const [isMobile] = useIsMobile(); - + const [hasSetCoinsSelected, setHasSetCoinsSelected] = useState(false); const [coinKeys, setCoinKeys] = useState([]); + const [formattedData, setFormattedData] = useState([]); const [dataOpenInterest, loadingOpenInterest, errorOpenInterest] = useRequest( REQUESTS[0], @@ -169,7 +171,7 @@ export default function VolumeChart() { dataKey={coinName} dot={false} name={coinName.toString()} - stroke={getTokenHex(coinName.toString())} + stroke={getTokenColor(coinName.toString())} key={'open-i-rate-line-' + i} /> ); diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 14e46f9..69ae99f 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -12,7 +12,7 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -26,7 +26,9 @@ import { yaxisFormatter, xAxisFormatter, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { cumulative_usd_volume, daily_usd_volume, @@ -50,6 +52,7 @@ export default function RetailVolumeChart() { const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coinKeys, setCoinKeys] = useState([]); const [dataCumulativeUsdVolume, loadingCumulativeUsdVolume, errorCumulativeUsdVolume] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -109,6 +112,7 @@ export default function RetailVolumeChart() { type FormattedVolumeData = any[]; //{ time: string, all: number, [coin: string]: number }; const formatVolumeByCoins = ( + CoinsSelected: string[], dataDailyUsdVolumeByCoin: VolumeData[], formattedCumulativeUsdVolume: { [key: string]: number }, formattedDailyVolumeByTime: { [key: string]: number } @@ -122,25 +126,21 @@ export default function RetailVolumeChart() { temp[data.time].all += data.daily_usd_volume; } - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj).sort( - ([, aVolume], [, bVolume]) => bVolume - aVolume - ); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); - + const selectedCoinData = (obj: { [coin: string]: number }) => { + const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) && coin !== "all"); + const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(top10Entries), + ...Object.fromEntries(selectedEntries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); + const selectedVolumes = selectedCoinData(volumes); return { time: new Date(time), - ...top10Volumes, + ...selectedVolumes, cumulative: formattedCumulativeUsdVolume[time as any], all: formattedDailyVolumeByTime[time as any], unit: '$', @@ -150,20 +150,10 @@ export default function RetailVolumeChart() { return result; }; - const extractUniqueCoins = (formattedVolumeData: FormattedVolumeData[]): string[] => { + const extractUniqueCoins = (formattedVolumeData: VolumeData[]): string[] => { const coinSet = new Set(); for (const data of formattedVolumeData) { - Object.keys(data).forEach((coin) => { - if ( - coin !== 'all' && - coin !== 'cumulative' && - coin !== 'time' && - coin !== 'other' && - coin !== 'unit' - ) { - coinSet.add(coin); - } - }); + coinSet.add(data.coin); } const coinsArray = Array.from(coinSet); if (coinsArray.includes('Other')) { @@ -211,10 +201,11 @@ export default function RetailVolumeChart() { return result; }; - const formatData = () => { + const formatData = (CoinsSelected: string[]) => { const formattedCumulativeVolumeByTime = formatCumulativeVolumeByTime(dataCumulativeUsdVolume); const formattedDailyVolumeByTime = formatDailyVolumeByTime(dataDailyUsdVolume); const formattedVolumeByCoins = formatVolumeByCoins( + CoinsSelected, dataDailyUsdVolumeByCoin, formattedCumulativeVolumeByTime, formattedDailyVolumeByTime @@ -224,7 +215,7 @@ export default function RetailVolumeChart() { formattedCumulativeVolumeByTime, formattedDailyVolumeByTime ); - setCoinKeys(extractUniqueCoins(formattedVolumeByCoins)); + setCoinKeys(extractUniqueCoins(dataDailyUsdVolumeByCoin)); setFormattedDataCoins(formattedVolumeByCoins); setFormattedDataMargin(formattedVolumeByCrossed); }; @@ -246,10 +237,12 @@ export default function RetailVolumeChart() { useEffect(() => { if (!loading || error) { - formatData(); + formatData(coinsSelected); } }, [loading, error]); + const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + return ( @@ -306,7 +300,7 @@ export default function RetailVolumeChart() { {dataMode === 'COINS' && ( <> - {coinKeys.map((coinName, i) => { + {coinsSelected.map((coinName, i) => { return ( diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 1dbb98d..b8f4768 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -14,7 +14,7 @@ import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import { useIsMobile } from '@/hooks/isMobile'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; import { tooltipFormatter, @@ -23,7 +23,9 @@ import { yaxisFormatterNumber, yaxisFormatterPercent, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { cumulative_new_users, daily_unique_users, @@ -71,6 +73,7 @@ const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_b export default function UniqueUsers() { const [isMobile] = useIsMobile(); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [formattedData, setFormattedData] = useState([]); const [coinKeys, setCoinKeys] = useState([]); @@ -93,6 +96,7 @@ export default function UniqueUsers() { const error = errorCumulativeNewUsers || errorDailyUniqueUsers || errorDailyUniqueUsersByCoin; const formatTradesByCoinAndTime = ( + CoinsSelected: string[], dataDailyUniqueUsersByCoin: DailyUniqueUsersByCoin[], uniqueUserTradeData: UniqueUserTradeData[], dataCumulativeNewUsers: CumulativeNewUsersData[] @@ -125,15 +129,12 @@ export default function UniqueUsers() { } ); - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj).sort( - ([, aVolume], [, bVolume]) => bVolume - aVolume - ); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); + const selectedCoinData = (obj: { [coin: string]: number }) => { + const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); + const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(top10Entries), + ...Object.fromEntries(selectedEntries), Other: otherVolume, }; }; @@ -141,57 +142,42 @@ export default function UniqueUsers() { return Object.values(temp).map(({ time, coins, ...rest }) => { return { time: new Date(time), - ...sortAndSliceTop10(coins), + ...selectedCoinData(coins), ...rest, unit: '%', }; }); }; - const extractUniqueCoins = (formattedVolumeData: GroupedTradeData[]): string[] => { + const extractUniqueCoins = (CoinData: any): string[] => { const coinSet = new Set(); - for (const data of formattedVolumeData) { - Object.keys(data).forEach((coin) => { - if ( - coin !== 'all' && - coin !== 'cumulative' && - coin !== 'time' && - coin !== 'other' && - coin !== 'unit' && - coin !== 'daily_unique_users' && - coin !== 'cumulative_unique_users' && - !coin.includes('daily_unique_users') - ) { - coinSet.add(coin); - } - }); + for (const data of CoinData) { + coinSet.add(data.coin); } const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } return coinsArray; }; - const formatData = () => { + const formatData = (CoinsSelector: string[]) => { const formattedData = formatTradesByCoinAndTime( + CoinsSelector, dataDailyUniqueUsersByCoin, dataDailyUniqueUsers, dataCumulativeNewUsers ); - const formattedUniqueCoinKeys = extractUniqueCoins(formattedData); + const formattedUniqueCoinKeys = extractUniqueCoins(dataDailyUniqueUsersByCoin); setFormattedData(formattedData); setCoinKeys(formattedUniqueCoinKeys); }; useEffect(() => { if (!loading && !error) { - formatData(); + formatData(coinsSelected); } }, [loading]); + const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + return ( @@ -242,7 +229,7 @@ export default function UniqueUsers() { }} /> - {coinKeys.map((coinName, i) => { + {coinsSelected.map((coinName, i) => { return ( diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index 81f08b2..28e990b 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -15,7 +15,7 @@ import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; import { useIsMobile } from '@/hooks/isMobile'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -29,7 +29,9 @@ import { yaxisFormatterNumber, xAxisFormatter, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { cumulative_trades, daily_trades, @@ -48,6 +50,7 @@ const REQUESTS = [ export default function VolumeChart() { const [isMobile] = useIsMobile(); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'USER'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); @@ -110,6 +113,7 @@ export default function VolumeChart() { type FormattedCoinTradesData = any[]; //{ time: string, all: number, [coin: string]: number }; const formatDailyTradesByCoins = ( + CoinsSelected: string[], dataDailyTradesByCoin: { coin: string; daily_trades: number; time: string }[], formattedCumulativeTradesByTime: { [key: string]: number } ): FormattedCoinTradesData[] => { @@ -122,22 +126,18 @@ export default function VolumeChart() { temp[data.time].all += data.daily_trades; } - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj).sort( - ([, aVolume], [, bVolume]) => bVolume - aVolume - ); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); - + const selectedCoinData = (obj: { [coin: string]: number }) => { + const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); + const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(top10Entries), + ...Object.fromEntries(selectedEntries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); + const top10Volumes = selectedCoinData(volumes); return { time: new Date(time), ...top10Volumes, @@ -148,21 +148,12 @@ export default function VolumeChart() { return result; }; - const extractUniqueCoins = (formattedCoinTradesData: FormattedCoinTradesData[]): string[] => { + const extractUniqueCoins = (CoinData: any): string[] => { const coinSet = new Set(); - for (const data of formattedCoinTradesData) { - Object.keys(data).forEach((coin) => { - if (coin !== 'all' && coin !== 'cumulative' && coin !== 'time' && coin !== 'unit') { - coinSet.add(coin); - } - }); + for (const data of CoinData) { + coinSet.add(data.coin); } const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } return coinsArray; }; @@ -206,9 +197,10 @@ export default function VolumeChart() { return Object.values(groupedByTime); }; - const formatData = () => { + const formatData = (CoinsSelected: string[]) => { const formattedCumulativeTradesByTime = formatTradesByTime(dataCumulativeTrades); const formattedTradesByCoins = formatDailyTradesByCoins( + CoinsSelected, dataDailyTradesByCoin, formattedCumulativeTradesByTime ); @@ -217,7 +209,7 @@ export default function VolumeChart() { formattedCumulativeTradesByTime ); setMaxAllValueUser(maxAllValueUser); - setCoinKeys(extractUniqueCoins(formattedTradesByCoins)); + setCoinKeys(extractUniqueCoins(dataDailyTradesByCoin)); setFormattedDataCoins(formattedTradesByCoins); setFormattedDataMarin(formattedTradesByMargin); }; @@ -239,10 +231,13 @@ export default function VolumeChart() { useEffect(() => { if (!loading && !error) { - formatData(); + formatData(coinsSelected); } }, [loading, dataMode]); + + const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + return ( - {coinKeys.map((coinName, i) => { + {coinsSelected.map((coinName, i) => { return ( diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index b6f1382..52dd4b0 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -14,7 +14,7 @@ import { useRequest } from '@/hooks/useRequest'; import { useIsMobile } from '@/hooks/isMobile'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { BRIGHT_GREEN, CHART_HEIGHT, YAXIS_WIDTH } from '../../../constants'; import { yaxisFormatter, @@ -22,14 +22,17 @@ import { tooltipFormatterCurrency, tooltipLabelFormatter, } from '../../../helpers'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + import { total_volume } from '../../../constants/api'; -import { getTokenHex } from '@/constants/tokens'; +import { getTokenColor, initialTokensSelectedWithOther } from '@/constants/tokens'; const REQUESTS = [total_volume]; export default function TotalVolumeChart() { const [isMobile] = useIsMobile(); const [formattedData, setFormattedData] = useState([]); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coins, setCoins] = useState([]); const [dataTotalVolume, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -48,9 +51,9 @@ export default function TotalVolumeChart() { Other: number; } - const makeFormattedData = (dataTotalVolume: TotalVolume[]): [MergedData[], string[]] => { + const makeFormattedData = (CoinsSelected: string[], dataTotalVolume: TotalVolume[]): [MergedData[], string[]] => { const map = new Map(); - const uniqueTopCoins = new Set(); + const uniqueCoins = new Set(); let cumulative = 0; dataTotalVolume.forEach((item: TotalVolume) => { @@ -80,43 +83,40 @@ export default function TotalVolumeChart() { key !== 'total' && key !== 'cumulative' && key !== 'other' && - key !== 'unit' - ); - const sortedCoinEntries = coinEntries.sort( - (a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1])) + key !== 'unit' && + key !== 'Other' ); - const topCoins = sortedCoinEntries.slice(0, 10).map(([coin]) => coin); - const otherCoins = sortedCoinEntries.slice(10); + const otherCoins = coinEntries.filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); - topCoins.forEach((coin) => uniqueTopCoins.add(coin)); + coinEntries.forEach(([coin]) => uniqueCoins.add(coin)); let otherTotal = 0; - otherCoins.forEach(([coin, value]) => { + otherCoins.forEach(([_, value]) => { otherTotal += value; - delete entry[coin]; }); entry.Other = otherTotal; }); const result = Array.from(map.values()); - uniqueTopCoins.add('Other'); - return [result, Array.from(uniqueTopCoins)]; + return [result, Array.from(uniqueCoins)]; }; - const formatData = () => { - const [newFormattedData, coins] = makeFormattedData(dataTotalVolume); + const formatData = (CoinsSelected: string[]) => { + const [newFormattedData, coins] = makeFormattedData(CoinsSelected, dataTotalVolume); setCoins(coins); setFormattedData(newFormattedData); }; useEffect(() => { if (!loading && !error) { - formatData(); + formatData(coinsSelected); } }, [loading, error]); + const coinSelectors = createCoinSelectorsWithFormatArg(coins, coinsSelected, setCoinsSelected, formatData); + return ( - + @@ -144,7 +144,7 @@ export default function TotalVolumeChart() { tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} /> - {coins.map((coin, i) => { + {coinsSelected.map((coin, i) => { return ( diff --git a/constants/tokens.ts b/constants/tokens.ts index d42bcc4..8da4f1a 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -1,37 +1,18 @@ -const TOKEN_COLORS: any = { - BNB: '#ebc509', - BTC: '#F2A900', - DOGE: '#cb9800', - ETH: '#8A94B1', - INJ: '#0386FA', - KPEPE: '#509844', - MATIC: '#8C35D5', - Other: '#BBBAC6', - SOL: '#C867F0', - AVAX: '#e74242', - LTC: '#CCCCCC', - ARB: '#FCA100', - LINK: '#81D2FD', - APE: '#4087BE', - ATOM: '#FCA100', - CFX: '#F3654E', - CRV: '#850087', - DYDX: '#BE586C', - FTM: '#568EC0', - GMX: '#59C782', - LDO: '#DB6ED7', - OP: '#7F0182', - RNDR: '#FFA300', - SNX: '#498548', - STX: '#578374', - SUI: '#6A807A', -}; +import * as CryptoJS from 'crypto-js'; -export const getTokenHex = (token: string) => { - const symbol = token.toUpperCase(); - if (TOKEN_COLORS[symbol]) { - return TOKEN_COLORS[symbol]; - } else { - return 'pink'; - } -}; + +export const initialTokensSelected = ['ETH', 'BTC', 'ARB', 'APE', 'ATOM', 'AVAX', 'BNB', 'COMP', 'CRV', 'DOGE']; +export const initialTokensSelectedWithOther = ['ETH', 'BTC', 'ARB', 'APE', 'ATOM', 'AVAX', 'BNB', 'COMP', 'CRV', 'DOGE','Other']; + +export function getTokenColor(inputString: string): string { + if (inputString == "Other") { + return "pink"; + } + // Use the CryptoJS library to get the MD5 hash of the string + let hash = CryptoJS.MD5("col"+inputString); + + // Convert the hash into a hex string + let color = hash.toString(CryptoJS.enc.Hex).substr(0, 6); + + return "#" + color; +} diff --git a/helpers/utils.ts b/helpers/utils.ts new file mode 100644 index 0000000..5228178 --- /dev/null +++ b/helpers/utils.ts @@ -0,0 +1,52 @@ +import { SetStateAction } from 'react'; +import { CoinSelector } from '../components/common/chartWrapper'; + +const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { + if (a.isChecked !== b.isChecked) { + return a.isChecked ? -1 : 1; + } + return a.name.localeCompare(b.name); + }; + + +export const createCoinSelectors = (coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, formatData: () => any) => { + return coinKeys.map((coinKey: string) => { + return { + name: coinKey, + event: () => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(); + setCoinsSelected(newCoinsSelected); + }, + isChecked: coinsSelected.includes(coinKey), + }; + }).sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); +} + +export const createCoinSelectorsWithFormatArg = (coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, formatData: (arg: string[]) => any) => { + return coinKeys.map((coinKey: string) => { + return { + name: coinKey, + event: () => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(newCoinsSelected); + setCoinsSelected(newCoinsSelected); + }, + isChecked: coinsSelected.includes(coinKey), + }; + }).sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); +} \ No newline at end of file diff --git a/hooks/isMobile.ts b/hooks/isMobile.ts index 46dbeee..f9cdffa 100644 --- a/hooks/isMobile.ts +++ b/hooks/isMobile.ts @@ -1,6 +1,9 @@ import { useEffect, useState } from 'react'; export function useIsMobile() { + if (typeof window === "undefined") { + return [false]; + } let [isMobile, setIsMobile] = useState(window.innerWidth < 700); useEffect(() => { setIsMobile(window.innerWidth < 700); diff --git a/package-lock.json b/package-lock.json index ed29c88..48aec2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,13 @@ "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@svgr/webpack": "^8.0.1", + "@types/crypto-js": "^4.1.1", "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-dom": "18.2.4", "@types/strftime": "^0.9.4", "axios": "^1.4.0", + "crypto-js": "^4.1.1", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "ethers": "^5.7.2", @@ -4400,6 +4402,11 @@ "integrity": "sha512-TnhDAntcJthcCMrR3OAKAUjgHyQgoms1yaBJepGv+BtXi8PLf8aX2L/NMCfofRTpVqW0bLklpGTsuqmUSCR2Uw==", "dev": true }, + "node_modules/@types/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==" + }, "node_modules/@types/d3-array": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", @@ -5246,6 +5253,11 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "node_modules/css-box-model": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", @@ -12389,6 +12401,11 @@ "integrity": "sha512-TnhDAntcJthcCMrR3OAKAUjgHyQgoms1yaBJepGv+BtXi8PLf8aX2L/NMCfofRTpVqW0bLklpGTsuqmUSCR2Uw==", "dev": true }, + "@types/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==" + }, "@types/d3-array": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", @@ -13028,6 +13045,11 @@ "which": "^2.0.1" } }, + "crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "css-box-model": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", diff --git a/package.json b/package.json index e056236..5f9ddb2 100644 --- a/package.json +++ b/package.json @@ -63,11 +63,13 @@ "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@svgr/webpack": "^8.0.1", + "@types/crypto-js": "^4.1.1", "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-dom": "18.2.4", "@types/strftime": "^0.9.4", "axios": "^1.4.0", + "crypto-js": "^4.1.1", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "ethers": "^5.7.2", From e89f4d10c3e73b2347d10f02104fd26822fd10a0 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Thu, 3 Aug 2023 04:44:26 +0900 Subject: [PATCH 30/59] Merge mobile changes (#12) * updated bar charts and colors * put coin selector sort in a util file --- components/common/chartWrapper/index.tsx | 6 +- components/home/charts/cumulative-inflow.tsx | 5 +- components/home/charts/cumulative-users.tsx | 10 ++- components/home/charts/fees.tsx | 6 +- components/home/charts/funding-rate.tsx | 41 +++---------- components/home/charts/hlp.tsx | 46 ++++++++------ components/home/charts/liquidator.tsx | 64 ++++++++++---------- components/home/charts/liquidity.tsx | 55 ++++++----------- components/home/charts/open-interest.tsx | 15 +++-- components/home/charts/retail-volume.tsx | 57 +++++++++-------- components/home/charts/trader-profit.tsx | 10 ++- components/home/charts/unique-users-coin.tsx | 64 +++++++++----------- components/home/charts/volume-num-trades.tsx | 57 +++++++++-------- components/home/charts/volume-total.tsx | 46 +++++++------- constants/tokens.ts | 53 ++++++---------- helpers/utils.ts | 52 ++++++++++++++++ hooks/isMobile.ts | 13 ++++ package-lock.json | 22 +++++++ package.json | 2 + 19 files changed, 331 insertions(+), 293 deletions(-) create mode 100644 helpers/utils.ts create mode 100644 hooks/isMobile.ts diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 2302d16..496a9f3 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -33,7 +33,7 @@ const Loader = () => ( ); function ChartWrapper(props: any) { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + let isMobile = props.isMobile; const { title, loading, controls, zIndex, coinSelectors } = props; const controlButtons = controls && @@ -92,9 +92,7 @@ function ChartWrapper(props: any) { {controlButtons} ) : ( - - {controlButtons} - + {controlButtons} )} )} diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 2acf73d..d4fe2e2 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -22,11 +22,12 @@ import { tooltipFormatterDate, } from '../../../helpers'; import { daily_inflow, cumulative_inflow } from '../../../constants/api'; +import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [daily_inflow, cumulative_inflow]; export default function CumulativeInflow() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [formattedData, setFormattedData] = useState([]); const [dataDailyInflow, loadingDailyInflow, errorDailyInflow] = useRequest( @@ -99,7 +100,7 @@ export default function CumulativeInflow() { }, [loading, errorDailyInflow]); return ( - + diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index f09b5e0..3003ffe 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -11,6 +11,7 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; @@ -29,7 +30,7 @@ import { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; export default function CumulativeUsers() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [formattedData, setFormattedData] = useState([]); @@ -74,7 +75,12 @@ export default function CumulativeUsers() { }, [loading, error]); return ( - + diff --git a/components/home/charts/fees.tsx b/components/home/charts/fees.tsx index c56e652..a893645 100644 --- a/components/home/charts/fees.tsx +++ b/components/home/charts/fees.tsx @@ -11,6 +11,7 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; @@ -25,7 +26,8 @@ import { total_accrued_fees } from '../../../constants/api'; const REQUESTS = [total_accrued_fees]; export default function Fees() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [formattedData, setFormattedData] = useState([]); const [dailyFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -68,7 +70,7 @@ export default function Fees() { }, [loading, error]); return ( - + diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 2ca2999..190f37b 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -11,6 +11,7 @@ import { import { useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT } from '../../../constants'; import { @@ -19,13 +20,15 @@ import { formatterPercent, tooltipFormatterDate, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectors } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; import { funding_rate } from '../../../constants/api'; const REQUESTS = [funding_rate]; export default function FundingRate() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [coinKeys, setCoinKeys] = useState([]); const [formattedData, setFormattedData] = useState([]); @@ -34,7 +37,7 @@ export default function FundingRate() { [], 'chart_data' ); - const [coinsSelected, setCoinsSelected] = useState(['ETH', 'BTC', 'ARB']); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const loading = loadingFundingRate; const error = errorFundingRate; @@ -113,34 +116,7 @@ export default function FundingRate() { } }, [loading, coinsSelected]); - const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { - if (a.isChecked !== b.isChecked) { - return a.isChecked ? -1 : 1; - } - return a.name.localeCompare(b.name); - }; - - const coinSelectors = coinKeys - .map((coinKey: string) => { - return { - name: coinKey, - event: () => - setCoinsSelected((coinsSelected) => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(); - return newCoinsSelected; - }), - isChecked: coinsSelected.includes(coinKey), - }; - }) - .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); + const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData) return ( @@ -189,7 +166,7 @@ export default function FundingRate() { dataKey={coinName.toString()} dot={false} name={coinName.toString()} - stroke={getTokenHex(coinName.toString())} + stroke={getTokenColor(coinName.toString())} key={'funding-rate-line-' + i} /> ); diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 6682717..d58eb8b 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -13,6 +13,8 @@ import { import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; + import ChartWrapper from '../../common/chartWrapper'; import { BLUE, BRIGHT_GREEN, CHART_HEIGHT, GREEN, ORANGE, RED } from '../../../constants'; import { @@ -22,14 +24,16 @@ import { yaxisFormatter, tooltipFormatterLongShort, } from '../../../helpers'; -import { getTokenHex } from '@/constants/tokens'; + +import { getTokenColor } from '@/constants/tokens'; import { asset_ctxs, hlp_liquidator_pnl, hlp_positions } from '@/constants/api'; const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; const DAY = 60 * 60 * 24 * 1000; export default function Hlp() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('PNL'); const [coins, setCoins] = useState([]); const [dataHlpPositions, loadingDataHlpPositions, errorDataHlpPositions] = useRequest( @@ -82,7 +86,7 @@ export default function Hlp() { const getOraclePxs = (assetCtxs: AssetCtx[]): Map => { const map = new Map(); assetCtxs.forEach((item) => { - map.set(item.coin + item.time, item.first_oracle_px); + map.set(item.coin + item.time, item.first_oracle_px); }); return map; }; @@ -128,7 +132,7 @@ export default function Hlp() { let { time, coin, daily_ntl } = item; if (!map.has(time)) { const pnl = hlpPnL.get(time)?.pnl || 0; - hedgedCumulativePnl += pnl; + hedgedCumulativePnl += pnl; map.set(time, { time: new Date(time), daily_ntl: 0, @@ -144,12 +148,12 @@ export default function Hlp() { existingEntry.daily_ntl += daily_ntl; const oraclePx = oraclePxs.get(coin + time); - let hedgedPnl = 0; + let hedgedPnl = 0; const nextTime = getNextTime(time); let oraclePxNext = oraclePxs.get(coin + nextTime); - - let prevTimeData = prevTime ? map.get(prevTime) : null; - let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; + + let prevTimeData = prevTime ? map.get(prevTime) : null; + let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; if (oraclePxNext && oraclePx && prevDayNtlPosition) { const pxChange = 1 - oraclePx / oraclePxNext; @@ -157,9 +161,9 @@ export default function Hlp() { hedgedPnl += pnl; } - existingEntry.hedged_pnl += hedgedPnl; + existingEntry.hedged_pnl += hedgedPnl; hedgedCumulativePnl += hedgedPnl; - existingEntry.hedged_cumulative_pnl = hedgedCumulativePnl; + existingEntry.hedged_cumulative_pnl = hedgedCumulativePnl; }); map.forEach((entry) => { @@ -237,7 +241,13 @@ export default function Hlp() { }, [loading, error, hlpPnL]); return ( - + @@ -295,7 +305,7 @@ export default function Hlp() { dataKey={coin} stackId='a' name={coin.toString()} - fill={getTokenHex(coin.toString())} + fill={getTokenColor(coin.toString())} key={i} maxBarSize={20} /> @@ -356,21 +366,19 @@ export default function Hlp() { )} - {dataMode === 'PNL' && ( - PNL over time - )} + {dataMode === 'PNL' && PNL over time} {dataMode === 'HEDGED' && ( - Hedged PNL over time. Hedge the previous day's position and add to today's PNL. + + Hedged PNL over time. Hedge the previous day's position and add to today's PNL. + )} - {dataMode === 'NET' && ( - Net notional position over time - )} + {dataMode === 'NET' && Net notional position over time} ); diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index d6a88aa..b34f45a 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -12,8 +12,9 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -30,7 +31,9 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { cumulative_liquidated_notional, daily_notional_liquidated_total, @@ -50,9 +53,10 @@ const REQUESTS = [ ]; export default function LiquidatorChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'PNL'>('COINS'); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); @@ -168,6 +172,7 @@ export default function LiquidatorChart() { type FormattedCoinTradesData = any[]; const formatDailyTradesByCoins = ( + CoinsSelected: string[], dataDailyTradesByCoin: { time: string; coin: string; daily_notional_liquidated: number }[], formattedCumulativeByTime: { [key: string]: number } ): FormattedCoinTradesData[] => { @@ -180,25 +185,21 @@ export default function LiquidatorChart() { temp[data.time].all += data.daily_notional_liquidated; } - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj).sort( - ([, aVolume], [, bVolume]) => bVolume - aVolume - ); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); - + const selectedCoinData = (obj: { [coin: string]: number }) => { + const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); + const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(top10Entries), + ...Object.fromEntries(selectedEntries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); + const selectedVolumes = selectedCoinData(volumes); return { time: new Date(time), - ...top10Volumes, + ...selectedVolumes, cumulative: formattedCumulativeByTime[time as any], unit: '', }; @@ -206,25 +207,17 @@ export default function LiquidatorChart() { return result; }; - const extractUniqueCoins = (formattedData: any[]): string[] => { + const extractUniqueCoins = (coinData: any[]): string[] => { const coinSet = new Set(); - for (const data of formattedData) { - Object.keys(data).forEach((coin) => { - if (coin !== 'time' && coin !== 'unit' && coin !== 'cumulative' && coin !== 'all') { - coinSet.add(coin); + for (const data of coinData) { + if (data.coin !== 'time' && data.coin !== 'unit' && data.coin !== 'cumulative' && data.coin !== 'all') { + coinSet.add(data.coin); } - }); - } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); } - return coinsArray; + return Array.from(coinSet); }; - const formatData = () => { + const formatData = (CoinsSelected: string[]) => { const formattedCumulativeLiquidatedByTime = formatCumulativeLiquidatedByTime(dataCumulativeLiquidated); const formattedVolumeByMargin = formatLiquidatedByMargin( @@ -232,6 +225,7 @@ export default function LiquidatorChart() { formattedCumulativeLiquidatedByTime ); const formattedDailyTradesByCoins = formatDailyTradesByCoins( + CoinsSelected, dataDailyLiquidatedByCoins, formattedCumulativeLiquidatedByTime ); @@ -240,7 +234,7 @@ export default function LiquidatorChart() { dataLiquidatorCumulativePnl ); setFormattedLiquidatorPnl(newFormattedLiquidatorPnl); - setCoinKeys(extractUniqueCoins(formattedDailyTradesByCoins)); + setCoinKeys(extractUniqueCoins(dataDailyLiquidatedByCoins)); setFormattedDataMargin(formattedVolumeByMargin); setFormattedDataCoins(formattedDailyTradesByCoins); console.log('dev formattedDailyTradesByCoins', formattedDailyTradesByCoins); @@ -268,7 +262,7 @@ export default function LiquidatorChart() { useEffect(() => { if (!loading && !error) { - formatData(); + formatData(coinsSelected); } }, [loading, error]); @@ -297,6 +291,10 @@ export default function LiquidatorChart() { return [-1 * Math.abs(maxCumulativePnl) * 1.1, Math.abs(maxCumulativePnl) * 1.1]; }; + + const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData) + + return ( @@ -334,8 +334,8 @@ export default function LiquidatorChart() { {dataMode === 'COINS' && ( <> - {coinKeys && - coinKeys.map((coinName, i) => { + { + coinsSelected.map((coinName, i) => { return ( diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 0bc7e92..128d18e 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -12,6 +12,7 @@ import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import { useIsMobile } from '@/hooks/isMobile'; import { CHART_HEIGHT } from '../../../constants'; import { tooltipFormatter, @@ -19,13 +20,16 @@ import { xAxisFormatter, formatterPercent, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectors } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; import { liquidity_by_coin } from '../../../constants/api'; const REQUESTS = [liquidity_by_coin]; export default function Liquidity() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [formattedData0, setFormattedData0] = useState([]); const [formattedData1000, setFormattedData1000] = useState([]); const [formattedData3000, setFormattedData3000] = useState([]); @@ -38,7 +42,7 @@ export default function Liquidity() { const [coinKeys10000, setCoinKeys10000] = useState([]); const [dataMode, setDataMode] = useState<'0' | '1000' | '3000' | '10000'>('0'); - const [coinsSelected, setCoinsSelected] = useState(['ETH', 'BTC', 'ARB']); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); const loading = loadingLiqudity; @@ -87,12 +91,12 @@ export default function Liquidity() { }; const extractCoins = (data: InputData): string[] => { - let coins = []; + let coins = []; for (let coin of Object.keys(data)) { - coins.push(coin); + coins.push(coin); } - return coins; - } + return coins; + }; const transformData = (data: InputData): OutputData => { // Filter data for each category by top 10 coins @@ -171,8 +175,8 @@ export default function Liquidity() { }; const formatData = () => { - const extractedCoinKeys = extractCoins(dataLiqudity); - setCoinKeys(extractedCoinKeys); + const extractedCoinKeys = extractCoins(dataLiqudity); + setCoinKeys(extractedCoinKeys); const formattedData = transformData(dataLiqudity); setFormattedData0(formattedData.median_slippage_0); setFormattedData1000(formattedData.median_slippage_1000); @@ -182,7 +186,7 @@ export default function Liquidity() { const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000); const formattedUniqueCoinKeys3000 = extractUniqueCoins(formattedData.median_slippage_3000); const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000); - + setCoinKeys0(formattedUniqueCoinKeys0); setCoinKeys1000(formattedUniqueCoinKeys1000); setCoinKeys3000(formattedUniqueCoinKeys3000); @@ -213,34 +217,8 @@ export default function Liquidity() { ? coinKeys3000 : coinKeys10000; - const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { - if (a.isChecked !== b.isChecked) { - return a.isChecked ? -1 : 1; - } - return a.name.localeCompare(b.name); - }; + const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); - const coinSelectors = coinKeys - .map((coinKey: string) => { - return { - name: coinKey, - event: () => - setCoinsSelected((coinsSelected) => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(); - return newCoinsSelected; - }), - isChecked: coinsSelected.includes(coinKey), - }; - }) - .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); return ( @@ -289,7 +268,7 @@ export default function Liquidity() { type='monotone' dataKey={`${coinName}`} name={coinName.toString()} - stroke={getTokenHex(coinName.toString())} + stroke={getTokenColor(coinName.toString())} key={i} dot={false} /> diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index 585ad34..f00bd61 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -11,7 +11,7 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { BRIGHT_GREEN, CHART_HEIGHT, GREEN, YAXIS_WIDTH } from '../../../constants'; import { xAxisFormatter, @@ -20,15 +20,18 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; + +import { getTokenColor } from '../../../constants/tokens'; import { open_interest } from '../../../constants/api'; +import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [open_interest]; export default function VolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); - + const [isMobile] = useIsMobile(); + const [hasSetCoinsSelected, setHasSetCoinsSelected] = useState(false); const [coinKeys, setCoinKeys] = useState([]); + const [formattedData, setFormattedData] = useState([]); const [dataOpenInterest, loadingOpenInterest, errorOpenInterest] = useRequest( REQUESTS[0], @@ -125,7 +128,7 @@ export default function VolumeChart() { }, [loading]); return ( - + @@ -168,7 +171,7 @@ export default function VolumeChart() { dataKey={coinName} dot={false} name={coinName.toString()} - stroke={getTokenHex(coinName.toString())} + stroke={getTokenColor(coinName.toString())} key={'open-i-rate-line-' + i} /> ); diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index ce0dc3e..69ae99f 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -12,7 +12,7 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -26,7 +26,9 @@ import { yaxisFormatter, xAxisFormatter, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { cumulative_usd_volume, daily_usd_volume, @@ -34,6 +36,7 @@ import { daily_usd_volume_by_crossed, daily_usd_volume_by_user, } from '../../../constants/api'; +import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [ cumulative_usd_volume, @@ -44,10 +47,12 @@ const REQUESTS = [ ]; export default function RetailVolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coinKeys, setCoinKeys] = useState([]); const [dataCumulativeUsdVolume, loadingCumulativeUsdVolume, errorCumulativeUsdVolume] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -107,6 +112,7 @@ export default function RetailVolumeChart() { type FormattedVolumeData = any[]; //{ time: string, all: number, [coin: string]: number }; const formatVolumeByCoins = ( + CoinsSelected: string[], dataDailyUsdVolumeByCoin: VolumeData[], formattedCumulativeUsdVolume: { [key: string]: number }, formattedDailyVolumeByTime: { [key: string]: number } @@ -120,25 +126,21 @@ export default function RetailVolumeChart() { temp[data.time].all += data.daily_usd_volume; } - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj).sort( - ([, aVolume], [, bVolume]) => bVolume - aVolume - ); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); - + const selectedCoinData = (obj: { [coin: string]: number }) => { + const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) && coin !== "all"); + const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(top10Entries), + ...Object.fromEntries(selectedEntries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); + const selectedVolumes = selectedCoinData(volumes); return { time: new Date(time), - ...top10Volumes, + ...selectedVolumes, cumulative: formattedCumulativeUsdVolume[time as any], all: formattedDailyVolumeByTime[time as any], unit: '$', @@ -148,20 +150,10 @@ export default function RetailVolumeChart() { return result; }; - const extractUniqueCoins = (formattedVolumeData: FormattedVolumeData[]): string[] => { + const extractUniqueCoins = (formattedVolumeData: VolumeData[]): string[] => { const coinSet = new Set(); for (const data of formattedVolumeData) { - Object.keys(data).forEach((coin) => { - if ( - coin !== 'all' && - coin !== 'cumulative' && - coin !== 'time' && - coin !== 'other' && - coin !== 'unit' - ) { - coinSet.add(coin); - } - }); + coinSet.add(data.coin); } const coinsArray = Array.from(coinSet); if (coinsArray.includes('Other')) { @@ -209,10 +201,11 @@ export default function RetailVolumeChart() { return result; }; - const formatData = () => { + const formatData = (CoinsSelected: string[]) => { const formattedCumulativeVolumeByTime = formatCumulativeVolumeByTime(dataCumulativeUsdVolume); const formattedDailyVolumeByTime = formatDailyVolumeByTime(dataDailyUsdVolume); const formattedVolumeByCoins = formatVolumeByCoins( + CoinsSelected, dataDailyUsdVolumeByCoin, formattedCumulativeVolumeByTime, formattedDailyVolumeByTime @@ -222,7 +215,7 @@ export default function RetailVolumeChart() { formattedCumulativeVolumeByTime, formattedDailyVolumeByTime ); - setCoinKeys(extractUniqueCoins(formattedVolumeByCoins)); + setCoinKeys(extractUniqueCoins(dataDailyUsdVolumeByCoin)); setFormattedDataCoins(formattedVolumeByCoins); setFormattedDataMargin(formattedVolumeByCrossed); }; @@ -244,10 +237,12 @@ export default function RetailVolumeChart() { useEffect(() => { if (!loading || error) { - formatData(); + formatData(coinsSelected); } }, [loading, error]); + const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + return ( {dataMode === 'COINS' && ( <> - {coinKeys.map((coinName, i) => { + {coinsSelected.map((coinName, i) => { return ( diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index 8f6c60e..11d6687 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -23,11 +23,12 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; +import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [cumulative_user_pnl, user_pnl]; export default function TradersProfitLossChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [data, setData] = useState(null); const [dataCumulativeUserPNL, loadingCumulativeUserPNL, errorCumulativeUserPNL] = useRequest( @@ -101,7 +102,12 @@ export default function TradersProfitLossChart() { }, [loading, error]); return ( - + diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 4ef7e44..b8f4768 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -12,7 +12,9 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; +import { useIsMobile } from '@/hooks/isMobile'; + +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; import { tooltipFormatter, @@ -21,7 +23,9 @@ import { yaxisFormatterNumber, yaxisFormatterPercent, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { cumulative_new_users, daily_unique_users, @@ -68,7 +72,8 @@ type TempGroupedTradeData = { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; export default function UniqueUsers() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [formattedData, setFormattedData] = useState([]); const [coinKeys, setCoinKeys] = useState([]); @@ -91,6 +96,7 @@ export default function UniqueUsers() { const error = errorCumulativeNewUsers || errorDailyUniqueUsers || errorDailyUniqueUsersByCoin; const formatTradesByCoinAndTime = ( + CoinsSelected: string[], dataDailyUniqueUsersByCoin: DailyUniqueUsersByCoin[], uniqueUserTradeData: UniqueUserTradeData[], dataCumulativeNewUsers: CumulativeNewUsersData[] @@ -123,15 +129,12 @@ export default function UniqueUsers() { } ); - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj).sort( - ([, aVolume], [, bVolume]) => bVolume - aVolume - ); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); + const selectedCoinData = (obj: { [coin: string]: number }) => { + const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); + const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(top10Entries), + ...Object.fromEntries(selectedEntries), Other: otherVolume, }; }; @@ -139,63 +142,50 @@ export default function UniqueUsers() { return Object.values(temp).map(({ time, coins, ...rest }) => { return { time: new Date(time), - ...sortAndSliceTop10(coins), + ...selectedCoinData(coins), ...rest, unit: '%', }; }); }; - const extractUniqueCoins = (formattedVolumeData: GroupedTradeData[]): string[] => { + const extractUniqueCoins = (CoinData: any): string[] => { const coinSet = new Set(); - for (const data of formattedVolumeData) { - Object.keys(data).forEach((coin) => { - if ( - coin !== 'all' && - coin !== 'cumulative' && - coin !== 'time' && - coin !== 'other' && - coin !== 'unit' && - coin !== 'daily_unique_users' && - coin !== 'cumulative_unique_users' && - !coin.includes('daily_unique_users') - ) { - coinSet.add(coin); - } - }); + for (const data of CoinData) { + coinSet.add(data.coin); } const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } return coinsArray; }; - const formatData = () => { + const formatData = (CoinsSelector: string[]) => { const formattedData = formatTradesByCoinAndTime( + CoinsSelector, dataDailyUniqueUsersByCoin, dataDailyUniqueUsers, dataCumulativeNewUsers ); - const formattedUniqueCoinKeys = extractUniqueCoins(formattedData); + const formattedUniqueCoinKeys = extractUniqueCoins(dataDailyUniqueUsersByCoin); setFormattedData(formattedData); setCoinKeys(formattedUniqueCoinKeys); }; useEffect(() => { if (!loading && !error) { - formatData(); + formatData(coinsSelected); } }, [loading]); + const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + return ( @@ -239,7 +229,7 @@ export default function UniqueUsers() { }} /> - {coinKeys.map((coinName, i) => { + {coinsSelected.map((coinName, i) => { return ( diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index 3f76a84..28e990b 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -13,7 +13,9 @@ import { import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; +import { useIsMobile } from '@/hooks/isMobile'; + +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -27,7 +29,9 @@ import { yaxisFormatterNumber, xAxisFormatter, } from '../../../helpers'; -import { getTokenHex } from '../../../constants/tokens'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { cumulative_trades, daily_trades, @@ -45,7 +49,8 @@ const REQUESTS = [ ]; export default function VolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'USER'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); @@ -108,6 +113,7 @@ export default function VolumeChart() { type FormattedCoinTradesData = any[]; //{ time: string, all: number, [coin: string]: number }; const formatDailyTradesByCoins = ( + CoinsSelected: string[], dataDailyTradesByCoin: { coin: string; daily_trades: number; time: string }[], formattedCumulativeTradesByTime: { [key: string]: number } ): FormattedCoinTradesData[] => { @@ -120,22 +126,18 @@ export default function VolumeChart() { temp[data.time].all += data.daily_trades; } - const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { - const sortedEntries = Object.entries(obj).sort( - ([, aVolume], [, bVolume]) => bVolume - aVolume - ); - const top10Entries = sortedEntries.slice(0, 10); - const otherEntries = sortedEntries.slice(10); - + const selectedCoinData = (obj: { [coin: string]: number }) => { + const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); + const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(top10Entries), + ...Object.fromEntries(selectedEntries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = sortAndSliceTop10(volumes); + const top10Volumes = selectedCoinData(volumes); return { time: new Date(time), ...top10Volumes, @@ -146,21 +148,12 @@ export default function VolumeChart() { return result; }; - const extractUniqueCoins = (formattedCoinTradesData: FormattedCoinTradesData[]): string[] => { + const extractUniqueCoins = (CoinData: any): string[] => { const coinSet = new Set(); - for (const data of formattedCoinTradesData) { - Object.keys(data).forEach((coin) => { - if (coin !== 'all' && coin !== 'cumulative' && coin !== 'time' && coin !== 'unit') { - coinSet.add(coin); - } - }); + for (const data of CoinData) { + coinSet.add(data.coin); } const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } return coinsArray; }; @@ -204,9 +197,10 @@ export default function VolumeChart() { return Object.values(groupedByTime); }; - const formatData = () => { + const formatData = (CoinsSelected: string[]) => { const formattedCumulativeTradesByTime = formatTradesByTime(dataCumulativeTrades); const formattedTradesByCoins = formatDailyTradesByCoins( + CoinsSelected, dataDailyTradesByCoin, formattedCumulativeTradesByTime ); @@ -215,7 +209,7 @@ export default function VolumeChart() { formattedCumulativeTradesByTime ); setMaxAllValueUser(maxAllValueUser); - setCoinKeys(extractUniqueCoins(formattedTradesByCoins)); + setCoinKeys(extractUniqueCoins(dataDailyTradesByCoin)); setFormattedDataCoins(formattedTradesByCoins); setFormattedDataMarin(formattedTradesByMargin); }; @@ -237,10 +231,13 @@ export default function VolumeChart() { useEffect(() => { if (!loading && !error) { - formatData(); + formatData(coinsSelected); } }, [loading, dataMode]); + + const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + return ( - {coinKeys.map((coinName, i) => { + {coinsSelected.map((coinName, i) => { return ( diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index ad286c2..52dd4b0 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -11,8 +11,10 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; +import { useIsMobile } from '@/hooks/isMobile'; + import { Box, Text, useMediaQuery } from '@chakra-ui/react'; -import ChartWrapper from '../../common/chartWrapper'; +import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { BRIGHT_GREEN, CHART_HEIGHT, YAXIS_WIDTH } from '../../../constants'; import { yaxisFormatter, @@ -20,14 +22,17 @@ import { tooltipFormatterCurrency, tooltipLabelFormatter, } from '../../../helpers'; +import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; + import { total_volume } from '../../../constants/api'; -import { getTokenHex } from '@/constants/tokens'; +import { getTokenColor, initialTokensSelectedWithOther } from '@/constants/tokens'; const REQUESTS = [total_volume]; export default function TotalVolumeChart() { - const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [isMobile] = useIsMobile(); const [formattedData, setFormattedData] = useState([]); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coins, setCoins] = useState([]); const [dataTotalVolume, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -46,9 +51,9 @@ export default function TotalVolumeChart() { Other: number; } - const makeFormattedData = (dataTotalVolume: TotalVolume[]): [MergedData[], string[]] => { + const makeFormattedData = (CoinsSelected: string[], dataTotalVolume: TotalVolume[]): [MergedData[], string[]] => { const map = new Map(); - const uniqueTopCoins = new Set(); + const uniqueCoins = new Set(); let cumulative = 0; dataTotalVolume.forEach((item: TotalVolume) => { @@ -78,43 +83,40 @@ export default function TotalVolumeChart() { key !== 'total' && key !== 'cumulative' && key !== 'other' && - key !== 'unit' + key !== 'unit' && + key !== 'Other' ); - const sortedCoinEntries = coinEntries.sort( - (a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1])) - ); - const topCoins = sortedCoinEntries.slice(0, 10).map(([coin]) => coin); - const otherCoins = sortedCoinEntries.slice(10); + const otherCoins = coinEntries.filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); - topCoins.forEach((coin) => uniqueTopCoins.add(coin)); + coinEntries.forEach(([coin]) => uniqueCoins.add(coin)); let otherTotal = 0; - otherCoins.forEach(([coin, value]) => { + otherCoins.forEach(([_, value]) => { otherTotal += value; - delete entry[coin]; }); entry.Other = otherTotal; }); const result = Array.from(map.values()); - uniqueTopCoins.add('Other'); - return [result, Array.from(uniqueTopCoins)]; + return [result, Array.from(uniqueCoins)]; }; - const formatData = () => { - const [newFormattedData, coins] = makeFormattedData(dataTotalVolume); + const formatData = (CoinsSelected: string[]) => { + const [newFormattedData, coins] = makeFormattedData(CoinsSelected, dataTotalVolume); setCoins(coins); setFormattedData(newFormattedData); }; useEffect(() => { if (!loading && !error) { - formatData(); + formatData(coinsSelected); } }, [loading, error]); + const coinSelectors = createCoinSelectorsWithFormatArg(coins, coinsSelected, setCoinsSelected, formatData); + return ( - + @@ -142,7 +144,7 @@ export default function TotalVolumeChart() { tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} /> - {coins.map((coin, i) => { + {coinsSelected.map((coin, i) => { return ( diff --git a/constants/tokens.ts b/constants/tokens.ts index d42bcc4..8da4f1a 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -1,37 +1,18 @@ -const TOKEN_COLORS: any = { - BNB: '#ebc509', - BTC: '#F2A900', - DOGE: '#cb9800', - ETH: '#8A94B1', - INJ: '#0386FA', - KPEPE: '#509844', - MATIC: '#8C35D5', - Other: '#BBBAC6', - SOL: '#C867F0', - AVAX: '#e74242', - LTC: '#CCCCCC', - ARB: '#FCA100', - LINK: '#81D2FD', - APE: '#4087BE', - ATOM: '#FCA100', - CFX: '#F3654E', - CRV: '#850087', - DYDX: '#BE586C', - FTM: '#568EC0', - GMX: '#59C782', - LDO: '#DB6ED7', - OP: '#7F0182', - RNDR: '#FFA300', - SNX: '#498548', - STX: '#578374', - SUI: '#6A807A', -}; +import * as CryptoJS from 'crypto-js'; -export const getTokenHex = (token: string) => { - const symbol = token.toUpperCase(); - if (TOKEN_COLORS[symbol]) { - return TOKEN_COLORS[symbol]; - } else { - return 'pink'; - } -}; + +export const initialTokensSelected = ['ETH', 'BTC', 'ARB', 'APE', 'ATOM', 'AVAX', 'BNB', 'COMP', 'CRV', 'DOGE']; +export const initialTokensSelectedWithOther = ['ETH', 'BTC', 'ARB', 'APE', 'ATOM', 'AVAX', 'BNB', 'COMP', 'CRV', 'DOGE','Other']; + +export function getTokenColor(inputString: string): string { + if (inputString == "Other") { + return "pink"; + } + // Use the CryptoJS library to get the MD5 hash of the string + let hash = CryptoJS.MD5("col"+inputString); + + // Convert the hash into a hex string + let color = hash.toString(CryptoJS.enc.Hex).substr(0, 6); + + return "#" + color; +} diff --git a/helpers/utils.ts b/helpers/utils.ts new file mode 100644 index 0000000..5228178 --- /dev/null +++ b/helpers/utils.ts @@ -0,0 +1,52 @@ +import { SetStateAction } from 'react'; +import { CoinSelector } from '../components/common/chartWrapper'; + +const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { + if (a.isChecked !== b.isChecked) { + return a.isChecked ? -1 : 1; + } + return a.name.localeCompare(b.name); + }; + + +export const createCoinSelectors = (coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, formatData: () => any) => { + return coinKeys.map((coinKey: string) => { + return { + name: coinKey, + event: () => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(); + setCoinsSelected(newCoinsSelected); + }, + isChecked: coinsSelected.includes(coinKey), + }; + }).sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); +} + +export const createCoinSelectorsWithFormatArg = (coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, formatData: (arg: string[]) => any) => { + return coinKeys.map((coinKey: string) => { + return { + name: coinKey, + event: () => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(newCoinsSelected); + setCoinsSelected(newCoinsSelected); + }, + isChecked: coinsSelected.includes(coinKey), + }; + }).sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); +} \ No newline at end of file diff --git a/hooks/isMobile.ts b/hooks/isMobile.ts new file mode 100644 index 0000000..f9cdffa --- /dev/null +++ b/hooks/isMobile.ts @@ -0,0 +1,13 @@ +import { useEffect, useState } from 'react'; + +export function useIsMobile() { + if (typeof window === "undefined") { + return [false]; + } + let [isMobile, setIsMobile] = useState(window.innerWidth < 700); + useEffect(() => { + setIsMobile(window.innerWidth < 700); + }, [window.innerWidth]); + + return [isMobile]; +} diff --git a/package-lock.json b/package-lock.json index ed29c88..48aec2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,13 @@ "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@svgr/webpack": "^8.0.1", + "@types/crypto-js": "^4.1.1", "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-dom": "18.2.4", "@types/strftime": "^0.9.4", "axios": "^1.4.0", + "crypto-js": "^4.1.1", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "ethers": "^5.7.2", @@ -4400,6 +4402,11 @@ "integrity": "sha512-TnhDAntcJthcCMrR3OAKAUjgHyQgoms1yaBJepGv+BtXi8PLf8aX2L/NMCfofRTpVqW0bLklpGTsuqmUSCR2Uw==", "dev": true }, + "node_modules/@types/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==" + }, "node_modules/@types/d3-array": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", @@ -5246,6 +5253,11 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "node_modules/css-box-model": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", @@ -12389,6 +12401,11 @@ "integrity": "sha512-TnhDAntcJthcCMrR3OAKAUjgHyQgoms1yaBJepGv+BtXi8PLf8aX2L/NMCfofRTpVqW0bLklpGTsuqmUSCR2Uw==", "dev": true }, + "@types/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==" + }, "@types/d3-array": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", @@ -13028,6 +13045,11 @@ "which": "^2.0.1" } }, + "crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "css-box-model": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", diff --git a/package.json b/package.json index e056236..5f9ddb2 100644 --- a/package.json +++ b/package.json @@ -63,11 +63,13 @@ "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@svgr/webpack": "^8.0.1", + "@types/crypto-js": "^4.1.1", "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-dom": "18.2.4", "@types/strftime": "^0.9.4", "axios": "^1.4.0", + "crypto-js": "^4.1.1", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "ethers": "^5.7.2", From 0f1bf1a78e85b3729d643cf250dba21325645452 Mon Sep 17 00:00:00 2001 From: chameleon Date: Wed, 2 Aug 2023 11:10:19 -0400 Subject: [PATCH 31/59] Change data format and add github/telegram --- components/common/footer/index.tsx | 4 ++-- components/home/charts/liquidity.tsx | 4 ++-- components/home/charts/unique-users-coin.tsx | 4 ++-- helpers/index.ts | 7 +++---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/components/common/footer/index.tsx b/components/common/footer/index.tsx index b625c1c..3e855b5 100644 --- a/components/common/footer/index.tsx +++ b/components/common/footer/index.tsx @@ -42,7 +42,7 @@ const Footer = () => { { { label = new Date(label * 1000); } const item = args && args[0] && args[0].payload && args[0]; - const dateFmtString = 'Total %d-%m-%y : '; - const date = strftime(dateFmtString, label); + const date = `Total ${label.toLocaleDateString()} : `; const all = item && item.payload.all; if (all) { return `${date} ${formatNumberWithOptions(all, { compact: true })}%`; @@ -127,8 +126,8 @@ export const tooltipLabelFormatter = (label: any, args: any, key?: string): any label = new Date(label * 1000); } const item = args && args[0] && args[0].payload && args[0]; - const dateFmtString = 'Total %d-%m-%y :'; - const date = strftime(dateFmtString, label); + const date = `Total ${label.toLocaleDateString()} : `; + let value; if (key) { value = item && item.payload[key]; From 9057031d760692e0665facaf00c83a434558e8b6 Mon Sep 17 00:00:00 2001 From: chameleon Date: Wed, 2 Aug 2023 11:50:55 -0400 Subject: [PATCH 32/59] small updates --- components/common/chartWrapper/index.tsx | 3 +-- components/home/charts/open-interest.tsx | 1 - styles/components/button.ts | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 496a9f3..2213590 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -84,7 +84,6 @@ function ChartWrapper(props: any) { w={{ xs: '100%', md: '100%' }} display='flex' justifyContent={{ xs: 'flex-start', md: 'flex-end' }} - mt={controls && controls.toggles && controls.toggles.length && '2'} mb='1rem' > {isMobile ? ( @@ -109,7 +108,7 @@ function ChartWrapper(props: any) { rightIcon={} variant='primary' fontSize={'14px'} - fontWeight={'bold'} + size='sm' > Select coins diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index f00bd61..8866fb7 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -29,7 +29,6 @@ const REQUESTS = [open_interest]; export default function VolumeChart() { const [isMobile] = useIsMobile(); - const [hasSetCoinsSelected, setHasSetCoinsSelected] = useState(false); const [coinKeys, setCoinKeys] = useState([]); const [formattedData, setFormattedData] = useState([]); diff --git a/styles/components/button.ts b/styles/components/button.ts index a695fd8..562f24a 100644 --- a/styles/components/button.ts +++ b/styles/components/button.ts @@ -2,7 +2,7 @@ import { lighten } from 'polished'; const Button = { baseStyle: { - fontWeight: '600', + fontWeight: '400', borderRadius: '24px', letterSpacing: '1px', padding: '1rem 1rem', From 4e996589c27fe4df52ea5a14f17116f66361d309 Mon Sep 17 00:00:00 2001 From: chameleon Date: Wed, 2 Aug 2023 14:26:22 -0400 Subject: [PATCH 33/59] more fixes --- components/common/chartWrapper/index.tsx | 4 +- components/home/charts/cumulative-inflow.tsx | 5 +-- components/home/charts/cumulative-users.tsx | 5 +-- components/home/charts/date-range.tsx | 4 +- components/home/charts/fees.tsx | 5 +-- components/home/charts/funding-rate.tsx | 5 +-- components/home/charts/hlp.tsx | 5 +-- components/home/charts/liquidator.tsx | 5 +-- components/home/charts/liquidity.tsx | 8 ++-- components/home/charts/open-interest.tsx | 7 ++- components/home/charts/retail-volume.tsx | 5 +-- components/home/charts/top-stats.tsx | 6 +-- components/home/charts/trader-profit.tsx | 5 +-- components/home/charts/unique-users-coin.tsx | 5 +-- components/home/charts/volume-num-trades.tsx | 5 +-- components/home/charts/volume-total.tsx | 8 ++-- components/home/main/index.tsx | 47 +++++++++++--------- constants/tokens.ts | 4 +- hooks/isMobile.ts | 13 ------ 19 files changed, 68 insertions(+), 83 deletions(-) delete mode 100644 hooks/isMobile.ts diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 2213590..02966d5 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -83,11 +83,11 @@ function ChartWrapper(props: any) { {isMobile ? ( - + {controlButtons} ) : ( diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index d4fe2e2..00dbfcf 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -22,12 +22,11 @@ import { tooltipFormatterDate, } from '../../../helpers'; import { daily_inflow, cumulative_inflow } from '../../../constants/api'; -import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [daily_inflow, cumulative_inflow]; -export default function CumulativeInflow() { - const [isMobile] = useIsMobile(); +export default function CumulativeInflow(props: any) { + const isMobile = props.isMobile; const [formattedData, setFormattedData] = useState([]); const [dataDailyInflow, loadingDailyInflow, errorDailyInflow] = useRequest( diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index 3003ffe..9774d4d 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -11,7 +11,6 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; @@ -29,8 +28,8 @@ import { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; -export default function CumulativeUsers() { - const [isMobile] = useIsMobile(); +export default function CumulativeUsers(props: any) { + const isMobile = props.isMobile; const [formattedData, setFormattedData] = useState([]); diff --git a/components/home/charts/date-range.tsx b/components/home/charts/date-range.tsx index b662943..75a24fb 100644 --- a/components/home/charts/date-range.tsx +++ b/components/home/charts/date-range.tsx @@ -3,13 +3,13 @@ import Select from 'react-dropdown-select'; import { Box } from '@chakra-ui/react'; import moment from 'moment'; import { DateRange } from 'react-date-range'; -import strftime from 'strftime'; +import strftime, { timezone } from 'strftime'; import { DataContext } from '../../../contexts/data'; import 'react-date-range/dist/styles.css'; import 'react-date-range/dist/theme/default.css'; const ALL_TIME_ID = 4; -const DATA_START_DATE = new Date('2023-06-14'); +const DATA_START_DATE = new Date('2023-06-14T20:00:00.000'); const DATE_NOW = new Date(); export const DateRangeSelect = () => { diff --git a/components/home/charts/fees.tsx b/components/home/charts/fees.tsx index a893645..731a73c 100644 --- a/components/home/charts/fees.tsx +++ b/components/home/charts/fees.tsx @@ -11,7 +11,6 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; @@ -25,8 +24,8 @@ import { total_accrued_fees } from '../../../constants/api'; const REQUESTS = [total_accrued_fees]; -export default function Fees() { - const [isMobile] = useIsMobile(); +export default function Fees(props: any) { + const isMobile = props.isMobile; const [formattedData, setFormattedData] = useState([]); const [dailyFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 190f37b..e8dc885 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -11,7 +11,6 @@ import { import { useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT } from '../../../constants'; import { @@ -27,8 +26,8 @@ import { funding_rate } from '../../../constants/api'; const REQUESTS = [funding_rate]; -export default function FundingRate() { - const [isMobile] = useIsMobile(); +export default function FundingRate(props: any) { + const isMobile = props.isMobile; const [coinKeys, setCoinKeys] = useState([]); const [formattedData, setFormattedData] = useState([]); diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index d58eb8b..95be51b 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -13,7 +13,6 @@ import { import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import ChartWrapper from '../../common/chartWrapper'; import { BLUE, BRIGHT_GREEN, CHART_HEIGHT, GREEN, ORANGE, RED } from '../../../constants'; @@ -31,8 +30,8 @@ const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; const DAY = 60 * 60 * 24 * 1000; -export default function Hlp() { - const [isMobile] = useIsMobile(); +export default function Hlp(props: any) { + const isMobile = props.isMobile; const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('PNL'); const [coins, setCoins] = useState([]); diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index b34f45a..8201b84 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -12,7 +12,6 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { @@ -52,8 +51,8 @@ const REQUESTS = [ cumulative_hlp_liquidator_pnl_false, ]; -export default function LiquidatorChart() { - const [isMobile] = useIsMobile(); +export default function LiquidatorChart(props: any) { + const isMobile = props.isMobile; const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'PNL'>('COINS'); const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index a8398fc..8c60c72 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -12,7 +12,6 @@ import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; -import { useIsMobile } from '@/hooks/isMobile'; import { CHART_HEIGHT } from '../../../constants'; import { tooltipFormatter, @@ -27,8 +26,8 @@ import { liquidity_by_coin } from '../../../constants/api'; const REQUESTS = [liquidity_by_coin]; -export default function Liquidity() { - const [isMobile] = useIsMobile(); +export default function Liquidity(props: any) { + const isMobile = props.isMobile; const [formattedData0, setFormattedData0] = useState([]); const [formattedData1000, setFormattedData1000] = useState([]); @@ -123,6 +122,9 @@ export default function Liquidity() { >(); for (let key in filteredData) { + if (!filteredData[key]) { + continue; + } filteredData[key].forEach((record) => { const { time, diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index 8866fb7..c5605dd 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -23,12 +23,11 @@ import { import { getTokenColor } from '../../../constants/tokens'; import { open_interest } from '../../../constants/api'; -import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [open_interest]; -export default function VolumeChart() { - const [isMobile] = useIsMobile(); +export default function VolumeChart(props: any) { + const isMobile = props.isMobile; const [coinKeys, setCoinKeys] = useState([]); const [formattedData, setFormattedData] = useState([]); @@ -117,7 +116,7 @@ export default function VolumeChart() { const groupedData = groupByTime(dataOpenInterest); const uniqueCoins = extractUniqueCoins(groupedData); setFormattedData(groupedData); - setCoinKeys(uniqueCoins); + setCoinKeys(uniqueCoins.sort()); }; useEffect(() => { diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 69ae99f..ef49d39 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -36,7 +36,6 @@ import { daily_usd_volume_by_crossed, daily_usd_volume_by_user, } from '../../../constants/api'; -import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [ cumulative_usd_volume, @@ -46,8 +45,8 @@ const REQUESTS = [ daily_usd_volume_by_user, ]; -export default function RetailVolumeChart() { - const [isMobile] = useIsMobile(); +export default function RetailVolumeChart(props: any) { + const isMobile = props.isMobile; const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); diff --git a/components/home/charts/top-stats.tsx b/components/home/charts/top-stats.tsx index 656cc46..280e52e 100644 --- a/components/home/charts/top-stats.tsx +++ b/components/home/charts/top-stats.tsx @@ -108,13 +108,13 @@ const TopStats = () => { {dataTotalDeposits - ? `$${formatNumber(dataTotalDeposits, 0)}` + ? `$${formatNumber(dataTotalDeposits + 245601, 0)}` : errorTotalDeposits ? 'Error' : null} {loadingTotalDeposits && } @@ -127,7 +127,7 @@ const TopStats = () => { : null} {loadingTotalWithdrawals && } diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index 11d6687..b2d1662 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -23,12 +23,11 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [cumulative_user_pnl, user_pnl]; -export default function TradersProfitLossChart() { - const [isMobile] = useIsMobile(); +export default function TradersProfitLossChart(props: any) { + const isMobile = props.isMobile; const [data, setData] = useState(null); const [dataCumulativeUserPNL, loadingCumulativeUserPNL, errorCumulativeUserPNL] = useRequest( diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 6c248c6..1d085d1 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -12,7 +12,6 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; @@ -71,8 +70,8 @@ type TempGroupedTradeData = { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; -export default function UniqueUsers() { - const [isMobile] = useIsMobile(); +export default function UniqueUsers(props: any) { + const isMobile = props.isMobile; const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [formattedData, setFormattedData] = useState([]); diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index 28e990b..94f8f9a 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -13,7 +13,6 @@ import { import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { @@ -48,8 +47,8 @@ const REQUESTS = [ cumulative_trades, ]; -export default function VolumeChart() { - const [isMobile] = useIsMobile(); +export default function VolumeChart(props: any) { + const isMobile = props.isMobile; const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'USER'>('COINS'); diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index 52dd4b0..72db529 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -11,7 +11,6 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; @@ -29,8 +28,9 @@ import { getTokenColor, initialTokensSelectedWithOther } from '@/constants/token const REQUESTS = [total_volume]; -export default function TotalVolumeChart() { - const [isMobile] = useIsMobile(); +export default function TotalVolumeChart(props: any) { + const isMobile = props.isMobile; + const [formattedData, setFormattedData] = useState([]); const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coins, setCoins] = useState([]); @@ -191,7 +191,7 @@ export default function TotalVolumeChart() { Top 10 Coins grouped daily and remaining coins grouped by Other. Volume tracked since - introduction of fees. + introduction of fees on 6/13/2023. diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index 756b2cd..bcfaa66 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -20,8 +20,15 @@ import Liquidity from '../charts/liquidity'; import HlpExposure from '../charts/hlp'; import TotalVolumeChart from '../charts/volume-total'; import UniqueUsers from '../charts/unique-users-coin'; +import { useEffect, useState } from 'react'; const Main = () => { + const [isMobile, setIsMobile] = useState(window.innerWidth < 700); + useEffect(() => { + setIsMobile(window.innerWidth < 700); + }, [window.innerWidth]); + + return ( { - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + - + diff --git a/constants/tokens.ts b/constants/tokens.ts index 8da4f1a..26d73b8 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -1,8 +1,8 @@ import * as CryptoJS from 'crypto-js'; -export const initialTokensSelected = ['ETH', 'BTC', 'ARB', 'APE', 'ATOM', 'AVAX', 'BNB', 'COMP', 'CRV', 'DOGE']; -export const initialTokensSelectedWithOther = ['ETH', 'BTC', 'ARB', 'APE', 'ATOM', 'AVAX', 'BNB', 'COMP', 'CRV', 'DOGE','Other']; +export const initialTokensSelected = ['APE', 'ARB', 'ATOM', 'AVAX', 'BNB', 'BTC', 'COMP', 'CRV', 'DOGE', 'ETH']; +export const initialTokensSelectedWithOther = ['APE', 'ARB', 'ATOM', 'AVAX', 'BNB', 'BTC', 'COMP', 'CRV', 'DOGE', 'ETH', 'Other']; export function getTokenColor(inputString: string): string { if (inputString == "Other") { diff --git a/hooks/isMobile.ts b/hooks/isMobile.ts deleted file mode 100644 index f9cdffa..0000000 --- a/hooks/isMobile.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useEffect, useState } from 'react'; - -export function useIsMobile() { - if (typeof window === "undefined") { - return [false]; - } - let [isMobile, setIsMobile] = useState(window.innerWidth < 700); - useEffect(() => { - setIsMobile(window.innerWidth < 700); - }, [window.innerWidth]); - - return [isMobile]; -} From d14d6be6724a9d64d38b30d6f0332636d54798d4 Mon Sep 17 00:00:00 2001 From: chameleon Date: Wed, 2 Aug 2023 15:54:37 -0400 Subject: [PATCH 34/59] prettify --- components/home/charts/funding-rate.tsx | 4 +- components/home/charts/liquidator.tsx | 62 ++++++----- components/home/charts/liquidity.tsx | 2 +- components/home/charts/retail-volume.tsx | 19 +++- components/home/charts/unique-users-coin.tsx | 21 ++-- components/home/charts/volume-num-trades.tsx | 24 +++-- components/home/charts/volume-total.tsx | 28 +++-- components/home/main/index.tsx | 1 - constants/tokens.ts | 46 +++++--- helpers/utils.ts | 105 +++++++++++-------- 10 files changed, 200 insertions(+), 112 deletions(-) diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index e8dc885..b8d39d3 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -19,7 +19,7 @@ import { formatterPercent, tooltipFormatterDate, } from '../../../helpers'; -import { createCoinSelectors } from "../../../helpers/utils"; +import { createCoinSelectors } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; import { funding_rate } from '../../../constants/api'; @@ -115,7 +115,7 @@ export default function FundingRate(props: any) { } }, [loading, coinsSelected]); - const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData) + const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); return ( { - const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); - const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const selectedEntries = Object.entries(obj).filter( + ([coin]) => CoinsSelected.includes(coin) || coin === 'all' + ); + const otherEntries = Object.entries(obj).filter( + ([coin]) => !CoinsSelected.includes(coin) && coin !== 'all' + ); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { ...Object.fromEntries(selectedEntries), @@ -209,9 +213,14 @@ export default function LiquidatorChart(props: any) { const extractUniqueCoins = (coinData: any[]): string[] => { const coinSet = new Set(); for (const data of coinData) { - if (data.coin !== 'time' && data.coin !== 'unit' && data.coin !== 'cumulative' && data.coin !== 'all') { - coinSet.add(data.coin); - } + if ( + data.coin !== 'time' && + data.coin !== 'unit' && + data.coin !== 'cumulative' && + data.coin !== 'all' + ) { + coinSet.add(data.coin); + } } return Array.from(coinSet); }; @@ -291,8 +300,12 @@ export default function LiquidatorChart(props: any) { return [-1 * Math.abs(maxCumulativePnl) * 1.1, Math.abs(maxCumulativePnl) * 1.1]; }; - const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData) - + const coinSelectors = createCoinSelectorsWithFormatArg( + coinKeys, + coinsSelected, + setCoinsSelected, + formatData + ); return ( @@ -333,22 +346,21 @@ export default function LiquidatorChart(props: any) { {dataMode === 'COINS' && ( <> - { - coinsSelected.map((coinName, i) => { - return ( - - ); - })} + {coinsSelected.map((coinName, i) => { + return ( + + ); + })} )} {dataMode === 'MARGIN' && ( diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 8c60c72..4fdb348 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -19,7 +19,7 @@ import { formatterPercent, tooltipFormatterDate, } from '../../../helpers'; -import { createCoinSelectors } from "../../../helpers/utils"; +import { createCoinSelectors } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; import { liquidity_by_coin } from '../../../constants/api'; diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index ef49d39..8a37bd8 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -26,7 +26,7 @@ import { yaxisFormatter, xAxisFormatter, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; +import { createCoinSelectorsWithFormatArg } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { @@ -126,8 +126,12 @@ export default function RetailVolumeChart(props: any) { } const selectedCoinData = (obj: { [coin: string]: number }) => { - const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) && coin !== "all"); - const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const selectedEntries = Object.entries(obj).filter( + ([coin]) => CoinsSelected.includes(coin) && coin !== 'all' + ); + const otherEntries = Object.entries(obj).filter( + ([coin]) => !CoinsSelected.includes(coin) && coin !== 'all' + ); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { ...Object.fromEntries(selectedEntries), @@ -240,7 +244,12 @@ export default function RetailVolumeChart(props: any) { } }, [loading, error]); - const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + const coinSelectors = createCoinSelectorsWithFormatArg( + coinKeys, + coinsSelected, + setCoinsSelected, + formatData + ); return ( diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 1d085d1..0c8b082 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -22,7 +22,7 @@ import { yaxisFormatterNumber, yaxisFormatterPercent, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; +import { createCoinSelectorsWithFormatArg } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { @@ -95,7 +95,7 @@ export default function UniqueUsers(props: any) { const error = errorCumulativeNewUsers || errorDailyUniqueUsers || errorDailyUniqueUsersByCoin; const formatTradesByCoinAndTime = ( - CoinsSelected: string[], + CoinsSelected: string[], dataDailyUniqueUsersByCoin: DailyUniqueUsersByCoin[], uniqueUserTradeData: UniqueUserTradeData[], dataCumulativeNewUsers: CumulativeNewUsersData[] @@ -129,8 +129,12 @@ export default function UniqueUsers(props: any) { ); const selectedCoinData = (obj: { [coin: string]: number }) => { - const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); - const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const selectedEntries = Object.entries(obj).filter( + ([coin]) => CoinsSelected.includes(coin) || coin === 'all' + ); + const otherEntries = Object.entries(obj).filter( + ([coin]) => !CoinsSelected.includes(coin) && coin !== 'all' + ); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { ...Object.fromEntries(selectedEntries), @@ -151,7 +155,7 @@ export default function UniqueUsers(props: any) { const extractUniqueCoins = (CoinData: any): string[] => { const coinSet = new Set(); for (const data of CoinData) { - coinSet.add(data.coin); + coinSet.add(data.coin); } const coinsArray = Array.from(coinSet); return coinsArray; @@ -175,7 +179,12 @@ export default function UniqueUsers(props: any) { } }, [loading]); - const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + const coinSelectors = createCoinSelectorsWithFormatArg( + coinKeys, + coinsSelected, + setCoinsSelected, + formatData + ); return ( { - const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); - const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const selectedEntries = Object.entries(obj).filter( + ([coin]) => CoinsSelected.includes(coin) || coin === 'all' + ); + const otherEntries = Object.entries(obj).filter( + ([coin]) => !CoinsSelected.includes(coin) && coin !== 'all' + ); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { ...Object.fromEntries(selectedEntries), @@ -150,7 +154,7 @@ export default function VolumeChart(props: any) { const extractUniqueCoins = (CoinData: any): string[] => { const coinSet = new Set(); for (const data of CoinData) { - coinSet.add(data.coin); + coinSet.add(data.coin); } const coinsArray = Array.from(coinSet); return coinsArray; @@ -234,8 +238,12 @@ export default function VolumeChart(props: any) { } }, [loading, dataMode]); - - const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); + const coinSelectors = createCoinSelectorsWithFormatArg( + coinKeys, + coinsSelected, + setCoinsSelected, + formatData + ); return ( { + const makeFormattedData = ( + CoinsSelected: string[], + dataTotalVolume: TotalVolume[] + ): [MergedData[], string[]] => { const map = new Map(); const uniqueCoins = new Set(); @@ -86,7 +89,9 @@ export default function TotalVolumeChart(props: any) { key !== 'unit' && key !== 'Other' ); - const otherCoins = coinEntries.filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const otherCoins = coinEntries.filter( + ([coin]) => !CoinsSelected.includes(coin) && coin !== 'all' + ); coinEntries.forEach(([coin]) => uniqueCoins.add(coin)); @@ -113,10 +118,21 @@ export default function TotalVolumeChart(props: any) { } }, [loading, error]); - const coinSelectors = createCoinSelectorsWithFormatArg(coins, coinsSelected, setCoinsSelected, formatData); + const coinSelectors = createCoinSelectorsWithFormatArg( + coins, + coinsSelected, + setCoinsSelected, + formatData + ); return ( - + diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index bcfaa66..38ac0ab 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -28,7 +28,6 @@ const Main = () => { setIsMobile(window.innerWidth < 700); }, [window.innerWidth]); - return ( { - if (a.isChecked !== b.isChecked) { - return a.isChecked ? -1 : 1; - } - return a.name.localeCompare(b.name); - }; + if (a.isChecked !== b.isChecked) { + return a.isChecked ? -1 : 1; + } + return a.name.localeCompare(b.name); +}; +export const createCoinSelectors = ( + coinKeys: string[], + coinsSelected: string[], + setCoinsSelected: (arg: string[]) => any, + formatData: () => any +) => { + return coinKeys + .map((coinKey: string) => { + return { + name: coinKey, + event: () => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(); + setCoinsSelected(newCoinsSelected); + }, + isChecked: coinsSelected.includes(coinKey), + }; + }) + .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); +}; -export const createCoinSelectors = (coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, formatData: () => any) => { - return coinKeys.map((coinKey: string) => { - return { - name: coinKey, - event: () => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(); - setCoinsSelected(newCoinsSelected); - }, - isChecked: coinsSelected.includes(coinKey), - }; - }).sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); -} - -export const createCoinSelectorsWithFormatArg = (coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, formatData: (arg: string[]) => any) => { - return coinKeys.map((coinKey: string) => { - return { - name: coinKey, - event: () => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(newCoinsSelected); - setCoinsSelected(newCoinsSelected); - }, - isChecked: coinsSelected.includes(coinKey), - }; - }).sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); -} \ No newline at end of file +export const createCoinSelectorsWithFormatArg = ( + coinKeys: string[], + coinsSelected: string[], + setCoinsSelected: (arg: string[]) => any, + formatData: (arg: string[]) => any +) => { + return coinKeys + .map((coinKey: string) => { + return { + name: coinKey, + event: () => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(newCoinsSelected); + setCoinsSelected(newCoinsSelected); + }, + isChecked: coinsSelected.includes(coinKey), + }; + }) + .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); +}; From 5b60f560362ad474b89fe9b632ae2442c7649b20 Mon Sep 17 00:00:00 2001 From: chameleon Date: Wed, 2 Aug 2023 16:00:39 -0400 Subject: [PATCH 35/59] apos fix --- components/home/charts/hlp.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 95be51b..0cca5c7 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -371,7 +371,7 @@ export default function Hlp(props: any) { {dataMode === 'HEDGED' && ( - Hedged PNL over time. Hedge the previous day's position and add to today's PNL. + Hedged PNL over time. Hedge the previous day's position and add to today's PNL. )} From 0ad951117a41b0dfc798911ee148eba29d71ae2a Mon Sep 17 00:00:00 2001 From: chameleon Date: Wed, 2 Aug 2023 18:26:09 -0400 Subject: [PATCH 36/59] fixes --- components/home/charts/hlp.tsx | 29 ++++++++-------- components/home/charts/liquidator.tsx | 4 +-- components/home/charts/retail-volume.tsx | 4 +-- components/home/charts/unique-users-coin.tsx | 4 +-- components/home/charts/volume-num-trades.tsx | 4 +-- components/home/charts/volume-total.tsx | 4 +-- components/home/main/index.tsx | 6 ++-- constants/tokens.ts | 17 +++------- helpers/utils.ts | 35 ++++---------------- 9 files changed, 36 insertions(+), 71 deletions(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 0cca5c7..7aa7a94 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -30,11 +30,11 @@ const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; const DAY = 60 * 60 * 24 * 1000; -export default function Hlp(props: any) { - const isMobile = props.isMobile; +type Props = {isMobile: boolean}; - const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('PNL'); - const [coins, setCoins] = useState([]); +export default function Hlp(props: any) { + const isMobile = props.isMobile; + const [dataHlpPositions, loadingDataHlpPositions, errorDataHlpPositions] = useRequest( REQUESTS[0], [], @@ -46,8 +46,9 @@ export default function Hlp(props: any) { [], 'chart_data' ); - const [oraclePxs, setOraclePxs] = useState>(new Map()); - const [hlpPnL, setHlpPnL] = useState>(new Map()); + const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('PNL'); + const [coins, setCoins] = useState([]); + const [formattedHlpPnL, setFormattedHlpPnL] = useState([]); const [formattedData, setFormattedData] = useState([]); @@ -120,12 +121,13 @@ export default function Hlp(props: any) { return yyyy + '-' + mm + '-' + dd + 'T00:00:00'; } - const makeFormattedData = (hlpPositions: HlpPosition[]): [GroupedData[], string[]] => { + const makeFormattedData = (hlpPositions: HlpPosition[], hlpPnL: Map): [GroupedData[], string[]] => { const map = new Map(); const uniqueTopCoins = new Set(); let prevTime: string | null = null; let hedgedCumulativePnl = 0; + const oraclePxs = getOraclePxs(assetCtxs); hlpPositions.forEach((item: HlpPosition) => { let { time, coin, daily_ntl } = item; @@ -150,7 +152,6 @@ export default function Hlp(props: any) { let hedgedPnl = 0; const nextTime = getNextTime(time); let oraclePxNext = oraclePxs.get(coin + nextTime); - let prevTimeData = prevTime ? map.get(prevTime) : null; let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; @@ -222,12 +223,9 @@ export default function Hlp(props: any) { const formatData = () => { if (dataHlpPositions && assetCtxs && dataHlpPnL) { - const newOraclePxs = getOraclePxs(assetCtxs); - setOraclePxs(newOraclePxs); const newHlpPnL = makeHlpPnl(dataHlpPnL); setFormattedHlpPnL(Array.from(newHlpPnL.values())); - setHlpPnL(newHlpPnL); - const [groupedData, coins] = makeFormattedData(dataHlpPositions); + const [groupedData, coins] = makeFormattedData(dataHlpPositions, newHlpPnL); setCoins(coins); setFormattedData(groupedData); } @@ -237,7 +235,7 @@ export default function Hlp(props: any) { if (!loading && !error) { formatData(); } - }, [loading, error, hlpPnL]); + }, [loading, error]); return ( {dataMode === 'HEDGED' && ( - Hedged PNL over time. Hedge the previous day's position and add to today's PNL. + Hedged PNL over time. Hedge the previous day's position and add to today's + PNL. )} @@ -381,4 +380,4 @@ export default function Hlp(props: any) { ); -} +} \ No newline at end of file diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index 360b012..19f2346 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -30,7 +30,7 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from '../../../helpers/utils'; +import { createCoinSelectors } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { @@ -300,7 +300,7 @@ export default function LiquidatorChart(props: any) { return [-1 * Math.abs(maxCumulativePnl) * 1.1, Math.abs(maxCumulativePnl) * 1.1]; }; - const coinSelectors = createCoinSelectorsWithFormatArg( + const coinSelectors = createCoinSelectors( coinKeys, coinsSelected, setCoinsSelected, diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 8a37bd8..678f880 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -26,7 +26,7 @@ import { yaxisFormatter, xAxisFormatter, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from '../../../helpers/utils'; +import { createCoinSelectors } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { @@ -244,7 +244,7 @@ export default function RetailVolumeChart(props: any) { } }, [loading, error]); - const coinSelectors = createCoinSelectorsWithFormatArg( + const coinSelectors = createCoinSelectors( coinKeys, coinsSelected, setCoinsSelected, diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 0c8b082..b1074a0 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -22,7 +22,7 @@ import { yaxisFormatterNumber, yaxisFormatterPercent, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from '../../../helpers/utils'; +import { createCoinSelectors } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { @@ -179,7 +179,7 @@ export default function UniqueUsers(props: any) { } }, [loading]); - const coinSelectors = createCoinSelectorsWithFormatArg( + const coinSelectors = createCoinSelectors( coinKeys, coinsSelected, setCoinsSelected, diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index 80ec8c2..1b98df1 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -28,7 +28,7 @@ import { yaxisFormatterNumber, xAxisFormatter, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from '../../../helpers/utils'; +import { createCoinSelectors } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { @@ -238,7 +238,7 @@ export default function VolumeChart(props: any) { } }, [loading, dataMode]); - const coinSelectors = createCoinSelectorsWithFormatArg( + const coinSelectors = createCoinSelectors( coinKeys, coinsSelected, setCoinsSelected, diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index c2bc502..ecfc690 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -21,7 +21,7 @@ import { tooltipFormatterCurrency, tooltipLabelFormatter, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from '../../../helpers/utils'; +import { createCoinSelectors } from '../../../helpers/utils'; import { total_volume } from '../../../constants/api'; import { getTokenColor, initialTokensSelectedWithOther } from '@/constants/tokens'; @@ -118,7 +118,7 @@ export default function TotalVolumeChart(props: any) { } }, [loading, error]); - const coinSelectors = createCoinSelectorsWithFormatArg( + const coinSelectors = createCoinSelectors( coins, coinsSelected, setCoinsSelected, diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index 38ac0ab..b4557ba 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -21,12 +21,10 @@ import HlpExposure from '../charts/hlp'; import TotalVolumeChart from '../charts/volume-total'; import UniqueUsers from '../charts/unique-users-coin'; import { useEffect, useState } from 'react'; +import { useMediaQuery } from '@chakra-ui/react'; const Main = () => { - const [isMobile, setIsMobile] = useState(window.innerWidth < 700); - useEffect(() => { - setIsMobile(window.innerWidth < 700); - }, [window.innerWidth]); + const isMobile = useMediaQuery('(max-width: 700px)'); return ( any, - formatData: () => any + formatData: ((arg: string[]) => any) | (() => any) ) => { return coinKeys .map((coinKey: string) => { @@ -27,39 +27,16 @@ export const createCoinSelectors = ( } else { newCoinsSelected.push(coinKey); } - formatData(); - setCoinsSelected(newCoinsSelected); - }, - isChecked: coinsSelected.includes(coinKey), - }; - }) - .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); -}; - -export const createCoinSelectorsWithFormatArg = ( - coinKeys: string[], - coinsSelected: string[], - setCoinsSelected: (arg: string[]) => any, - formatData: (arg: string[]) => any -) => { - return coinKeys - .map((coinKey: string) => { - return { - name: coinKey, - event: () => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); + if (formatData.length > 0) { + formatData(newCoinsSelected); } else { - newCoinsSelected.push(coinKey); + const noArgsFormatData = formatData as () => any; + noArgsFormatData(); } - formatData(newCoinsSelected); setCoinsSelected(newCoinsSelected); }, isChecked: coinsSelected.includes(coinKey), }; }) .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); -}; +}; \ No newline at end of file From 8be1e73eddf7bfb23dd300427f5908b0ce612449 Mon Sep 17 00:00:00 2001 From: chameleon Date: Thu, 3 Aug 2023 11:56:11 -0400 Subject: [PATCH 37/59] Revert "Merge mobile changes (#12)" This reverts commit e89f4d10c3e73b2347d10f02104fd26822fd10a0. --- components/common/chartWrapper/index.tsx | 6 +- components/home/charts/cumulative-inflow.tsx | 5 +- components/home/charts/cumulative-users.tsx | 10 +-- components/home/charts/fees.tsx | 6 +- components/home/charts/funding-rate.tsx | 41 ++++++++++--- components/home/charts/hlp.tsx | 46 ++++++-------- components/home/charts/liquidator.tsx | 64 ++++++++++---------- components/home/charts/liquidity.tsx | 55 +++++++++++------ components/home/charts/open-interest.tsx | 15 ++--- components/home/charts/retail-volume.tsx | 57 ++++++++--------- components/home/charts/trader-profit.tsx | 10 +-- components/home/charts/unique-users-coin.tsx | 64 +++++++++++--------- components/home/charts/volume-num-trades.tsx | 57 ++++++++--------- components/home/charts/volume-total.tsx | 46 +++++++------- constants/tokens.ts | 53 ++++++++++------ helpers/utils.ts | 52 ---------------- hooks/isMobile.ts | 13 ---- package-lock.json | 22 ------- package.json | 2 - 19 files changed, 293 insertions(+), 331 deletions(-) delete mode 100644 helpers/utils.ts delete mode 100644 hooks/isMobile.ts diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 496a9f3..2302d16 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -33,7 +33,7 @@ const Loader = () => ( ); function ChartWrapper(props: any) { - let isMobile = props.isMobile; + const [isMobile] = useMediaQuery('(max-width: 700px)'); const { title, loading, controls, zIndex, coinSelectors } = props; const controlButtons = controls && @@ -92,7 +92,9 @@ function ChartWrapper(props: any) { {controlButtons} ) : ( - {controlButtons} + + {controlButtons} + )} )} diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index d4fe2e2..2acf73d 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -22,12 +22,11 @@ import { tooltipFormatterDate, } from '../../../helpers'; import { daily_inflow, cumulative_inflow } from '../../../constants/api'; -import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [daily_inflow, cumulative_inflow]; export default function CumulativeInflow() { - const [isMobile] = useIsMobile(); + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [formattedData, setFormattedData] = useState([]); const [dataDailyInflow, loadingDailyInflow, errorDailyInflow] = useRequest( @@ -100,7 +99,7 @@ export default function CumulativeInflow() { }, [loading, errorDailyInflow]); return ( - + diff --git a/components/home/charts/cumulative-users.tsx b/components/home/charts/cumulative-users.tsx index 3003ffe..f09b5e0 100644 --- a/components/home/charts/cumulative-users.tsx +++ b/components/home/charts/cumulative-users.tsx @@ -11,7 +11,6 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; @@ -30,7 +29,7 @@ import { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; export default function CumulativeUsers() { - const [isMobile] = useIsMobile(); + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [formattedData, setFormattedData] = useState([]); @@ -75,12 +74,7 @@ export default function CumulativeUsers() { }, [loading, error]); return ( - + diff --git a/components/home/charts/fees.tsx b/components/home/charts/fees.tsx index a893645..c56e652 100644 --- a/components/home/charts/fees.tsx +++ b/components/home/charts/fees.tsx @@ -11,7 +11,6 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN } from '../../../constants'; @@ -26,8 +25,7 @@ import { total_accrued_fees } from '../../../constants/api'; const REQUESTS = [total_accrued_fees]; export default function Fees() { - const [isMobile] = useIsMobile(); - + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [formattedData, setFormattedData] = useState([]); const [dailyFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -70,7 +68,7 @@ export default function Fees() { }, [loading, error]); return ( - + diff --git a/components/home/charts/funding-rate.tsx b/components/home/charts/funding-rate.tsx index 190f37b..2ca2999 100644 --- a/components/home/charts/funding-rate.tsx +++ b/components/home/charts/funding-rate.tsx @@ -11,7 +11,6 @@ import { import { useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; import { CHART_HEIGHT } from '../../../constants'; import { @@ -20,15 +19,13 @@ import { formatterPercent, tooltipFormatterDate, } from '../../../helpers'; -import { createCoinSelectors } from "../../../helpers/utils"; - -import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; +import { getTokenHex } from '../../../constants/tokens'; import { funding_rate } from '../../../constants/api'; const REQUESTS = [funding_rate]; export default function FundingRate() { - const [isMobile] = useIsMobile(); + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [coinKeys, setCoinKeys] = useState([]); const [formattedData, setFormattedData] = useState([]); @@ -37,7 +34,7 @@ export default function FundingRate() { [], 'chart_data' ); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); + const [coinsSelected, setCoinsSelected] = useState(['ETH', 'BTC', 'ARB']); const loading = loadingFundingRate; const error = errorFundingRate; @@ -116,7 +113,34 @@ export default function FundingRate() { } }, [loading, coinsSelected]); - const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData) + const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { + if (a.isChecked !== b.isChecked) { + return a.isChecked ? -1 : 1; + } + return a.name.localeCompare(b.name); + }; + + const coinSelectors = coinKeys + .map((coinKey: string) => { + return { + name: coinKey, + event: () => + setCoinsSelected((coinsSelected) => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(); + return newCoinsSelected; + }), + isChecked: coinsSelected.includes(coinKey), + }; + }) + .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); return ( @@ -166,7 +189,7 @@ export default function FundingRate() { dataKey={coinName.toString()} dot={false} name={coinName.toString()} - stroke={getTokenColor(coinName.toString())} + stroke={getTokenHex(coinName.toString())} key={'funding-rate-line-' + i} /> ); diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index d58eb8b..6682717 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -13,8 +13,6 @@ import { import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; - import ChartWrapper from '../../common/chartWrapper'; import { BLUE, BRIGHT_GREEN, CHART_HEIGHT, GREEN, ORANGE, RED } from '../../../constants'; import { @@ -24,16 +22,14 @@ import { yaxisFormatter, tooltipFormatterLongShort, } from '../../../helpers'; - -import { getTokenColor } from '@/constants/tokens'; +import { getTokenHex } from '@/constants/tokens'; import { asset_ctxs, hlp_liquidator_pnl, hlp_positions } from '@/constants/api'; const REQUESTS = [hlp_positions, asset_ctxs, hlp_liquidator_pnl]; const DAY = 60 * 60 * 24 * 1000; export default function Hlp() { - const [isMobile] = useIsMobile(); - + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [dataMode, setDataMode] = useState<'COINS' | 'NET' | 'PNL' | 'HEDGED'>('PNL'); const [coins, setCoins] = useState([]); const [dataHlpPositions, loadingDataHlpPositions, errorDataHlpPositions] = useRequest( @@ -86,7 +82,7 @@ export default function Hlp() { const getOraclePxs = (assetCtxs: AssetCtx[]): Map => { const map = new Map(); assetCtxs.forEach((item) => { - map.set(item.coin + item.time, item.first_oracle_px); + map.set(item.coin + item.time, item.first_oracle_px); }); return map; }; @@ -132,7 +128,7 @@ export default function Hlp() { let { time, coin, daily_ntl } = item; if (!map.has(time)) { const pnl = hlpPnL.get(time)?.pnl || 0; - hedgedCumulativePnl += pnl; + hedgedCumulativePnl += pnl; map.set(time, { time: new Date(time), daily_ntl: 0, @@ -148,12 +144,12 @@ export default function Hlp() { existingEntry.daily_ntl += daily_ntl; const oraclePx = oraclePxs.get(coin + time); - let hedgedPnl = 0; + let hedgedPnl = 0; const nextTime = getNextTime(time); let oraclePxNext = oraclePxs.get(coin + nextTime); - - let prevTimeData = prevTime ? map.get(prevTime) : null; - let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; + + let prevTimeData = prevTime ? map.get(prevTime) : null; + let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; if (oraclePxNext && oraclePx && prevDayNtlPosition) { const pxChange = 1 - oraclePx / oraclePxNext; @@ -161,9 +157,9 @@ export default function Hlp() { hedgedPnl += pnl; } - existingEntry.hedged_pnl += hedgedPnl; + existingEntry.hedged_pnl += hedgedPnl; hedgedCumulativePnl += hedgedPnl; - existingEntry.hedged_cumulative_pnl = hedgedCumulativePnl; + existingEntry.hedged_cumulative_pnl = hedgedCumulativePnl; }); map.forEach((entry) => { @@ -241,13 +237,7 @@ export default function Hlp() { }, [loading, error, hlpPnL]); return ( - + @@ -305,7 +295,7 @@ export default function Hlp() { dataKey={coin} stackId='a' name={coin.toString()} - fill={getTokenColor(coin.toString())} + fill={getTokenHex(coin.toString())} key={i} maxBarSize={20} /> @@ -366,19 +356,21 @@ export default function Hlp() { )} - {dataMode === 'PNL' && PNL over time} + {dataMode === 'PNL' && ( + PNL over time + )} {dataMode === 'HEDGED' && ( - - Hedged PNL over time. Hedge the previous day's position and add to today's PNL. - + Hedged PNL over time. Hedge the previous day's position and add to today's PNL. )} - {dataMode === 'NET' && Net notional position over time} + {dataMode === 'NET' && ( + Net notional position over time + )} ); diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index b34f45a..d6a88aa 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -12,9 +12,8 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -31,9 +30,7 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; - -import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; +import { getTokenHex } from '../../../constants/tokens'; import { cumulative_liquidated_notional, daily_notional_liquidated_total, @@ -53,10 +50,9 @@ const REQUESTS = [ ]; export default function LiquidatorChart() { - const [isMobile] = useIsMobile(); + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'PNL'>('COINS'); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); @@ -172,7 +168,6 @@ export default function LiquidatorChart() { type FormattedCoinTradesData = any[]; const formatDailyTradesByCoins = ( - CoinsSelected: string[], dataDailyTradesByCoin: { time: string; coin: string; daily_notional_liquidated: number }[], formattedCumulativeByTime: { [key: string]: number } ): FormattedCoinTradesData[] => { @@ -185,21 +180,25 @@ export default function LiquidatorChart() { temp[data.time].all += data.daily_notional_liquidated; } - const selectedCoinData = (obj: { [coin: string]: number }) => { - const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); - const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { + const sortedEntries = Object.entries(obj).sort( + ([, aVolume], [, bVolume]) => bVolume - aVolume + ); + const top10Entries = sortedEntries.slice(0, 10); + const otherEntries = sortedEntries.slice(10); + const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(selectedEntries), + ...Object.fromEntries(top10Entries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const selectedVolumes = selectedCoinData(volumes); + const top10Volumes = sortAndSliceTop10(volumes); return { time: new Date(time), - ...selectedVolumes, + ...top10Volumes, cumulative: formattedCumulativeByTime[time as any], unit: '', }; @@ -207,17 +206,25 @@ export default function LiquidatorChart() { return result; }; - const extractUniqueCoins = (coinData: any[]): string[] => { + const extractUniqueCoins = (formattedData: any[]): string[] => { const coinSet = new Set(); - for (const data of coinData) { - if (data.coin !== 'time' && data.coin !== 'unit' && data.coin !== 'cumulative' && data.coin !== 'all') { - coinSet.add(data.coin); + for (const data of formattedData) { + Object.keys(data).forEach((coin) => { + if (coin !== 'time' && coin !== 'unit' && coin !== 'cumulative' && coin !== 'all') { + coinSet.add(coin); } + }); + } + const coinsArray = Array.from(coinSet); + if (coinsArray.includes('Other')) { + const index = coinsArray.indexOf('Other'); + coinsArray.splice(index, 1); + coinsArray.push('Other'); } - return Array.from(coinSet); + return coinsArray; }; - const formatData = (CoinsSelected: string[]) => { + const formatData = () => { const formattedCumulativeLiquidatedByTime = formatCumulativeLiquidatedByTime(dataCumulativeLiquidated); const formattedVolumeByMargin = formatLiquidatedByMargin( @@ -225,7 +232,6 @@ export default function LiquidatorChart() { formattedCumulativeLiquidatedByTime ); const formattedDailyTradesByCoins = formatDailyTradesByCoins( - CoinsSelected, dataDailyLiquidatedByCoins, formattedCumulativeLiquidatedByTime ); @@ -234,7 +240,7 @@ export default function LiquidatorChart() { dataLiquidatorCumulativePnl ); setFormattedLiquidatorPnl(newFormattedLiquidatorPnl); - setCoinKeys(extractUniqueCoins(dataDailyLiquidatedByCoins)); + setCoinKeys(extractUniqueCoins(formattedDailyTradesByCoins)); setFormattedDataMargin(formattedVolumeByMargin); setFormattedDataCoins(formattedDailyTradesByCoins); console.log('dev formattedDailyTradesByCoins', formattedDailyTradesByCoins); @@ -262,7 +268,7 @@ export default function LiquidatorChart() { useEffect(() => { if (!loading && !error) { - formatData(coinsSelected); + formatData(); } }, [loading, error]); @@ -291,10 +297,6 @@ export default function LiquidatorChart() { return [-1 * Math.abs(maxCumulativePnl) * 1.1, Math.abs(maxCumulativePnl) * 1.1]; }; - - const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData) - - return ( @@ -334,8 +334,8 @@ export default function LiquidatorChart() { {dataMode === 'COINS' && ( <> - { - coinsSelected.map((coinName, i) => { + {coinKeys && + coinKeys.map((coinName, i) => { return ( diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 128d18e..0bc7e92 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -12,7 +12,6 @@ import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; -import { useIsMobile } from '@/hooks/isMobile'; import { CHART_HEIGHT } from '../../../constants'; import { tooltipFormatter, @@ -20,16 +19,13 @@ import { xAxisFormatter, formatterPercent, } from '../../../helpers'; -import { createCoinSelectors } from "../../../helpers/utils"; - -import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; +import { getTokenHex } from '../../../constants/tokens'; import { liquidity_by_coin } from '../../../constants/api'; const REQUESTS = [liquidity_by_coin]; export default function Liquidity() { - const [isMobile] = useIsMobile(); - + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [formattedData0, setFormattedData0] = useState([]); const [formattedData1000, setFormattedData1000] = useState([]); const [formattedData3000, setFormattedData3000] = useState([]); @@ -42,7 +38,7 @@ export default function Liquidity() { const [coinKeys10000, setCoinKeys10000] = useState([]); const [dataMode, setDataMode] = useState<'0' | '1000' | '3000' | '10000'>('0'); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); + const [coinsSelected, setCoinsSelected] = useState(['ETH', 'BTC', 'ARB']); const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); const loading = loadingLiqudity; @@ -91,12 +87,12 @@ export default function Liquidity() { }; const extractCoins = (data: InputData): string[] => { - let coins = []; + let coins = []; for (let coin of Object.keys(data)) { - coins.push(coin); + coins.push(coin); } - return coins; - }; + return coins; + } const transformData = (data: InputData): OutputData => { // Filter data for each category by top 10 coins @@ -175,8 +171,8 @@ export default function Liquidity() { }; const formatData = () => { - const extractedCoinKeys = extractCoins(dataLiqudity); - setCoinKeys(extractedCoinKeys); + const extractedCoinKeys = extractCoins(dataLiqudity); + setCoinKeys(extractedCoinKeys); const formattedData = transformData(dataLiqudity); setFormattedData0(formattedData.median_slippage_0); setFormattedData1000(formattedData.median_slippage_1000); @@ -186,7 +182,7 @@ export default function Liquidity() { const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000); const formattedUniqueCoinKeys3000 = extractUniqueCoins(formattedData.median_slippage_3000); const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000); - + setCoinKeys0(formattedUniqueCoinKeys0); setCoinKeys1000(formattedUniqueCoinKeys1000); setCoinKeys3000(formattedUniqueCoinKeys3000); @@ -217,8 +213,34 @@ export default function Liquidity() { ? coinKeys3000 : coinKeys10000; - const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); + const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { + if (a.isChecked !== b.isChecked) { + return a.isChecked ? -1 : 1; + } + return a.name.localeCompare(b.name); + }; + const coinSelectors = coinKeys + .map((coinKey: string) => { + return { + name: coinKey, + event: () => + setCoinsSelected((coinsSelected) => { + let newCoinsSelected = coinsSelected; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = coinsSelected.filter((e) => { + return e !== coinKey; + }); + } else { + newCoinsSelected.push(coinKey); + } + formatData(); + return newCoinsSelected; + }), + isChecked: coinsSelected.includes(coinKey), + }; + }) + .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); return ( @@ -268,7 +289,7 @@ export default function Liquidity() { type='monotone' dataKey={`${coinName}`} name={coinName.toString()} - stroke={getTokenColor(coinName.toString())} + stroke={getTokenHex(coinName.toString())} key={i} dot={false} /> diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index f00bd61..585ad34 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -11,7 +11,7 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import ChartWrapper from '../../common/chartWrapper'; import { BRIGHT_GREEN, CHART_HEIGHT, GREEN, YAXIS_WIDTH } from '../../../constants'; import { xAxisFormatter, @@ -20,18 +20,15 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; - -import { getTokenColor } from '../../../constants/tokens'; +import { getTokenHex } from '../../../constants/tokens'; import { open_interest } from '../../../constants/api'; -import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [open_interest]; export default function VolumeChart() { - const [isMobile] = useIsMobile(); - const [hasSetCoinsSelected, setHasSetCoinsSelected] = useState(false); - const [coinKeys, setCoinKeys] = useState([]); + const [isMobile] = useMediaQuery('(max-width: 700px)'); + const [coinKeys, setCoinKeys] = useState([]); const [formattedData, setFormattedData] = useState([]); const [dataOpenInterest, loadingOpenInterest, errorOpenInterest] = useRequest( REQUESTS[0], @@ -128,7 +125,7 @@ export default function VolumeChart() { }, [loading]); return ( - + @@ -171,7 +168,7 @@ export default function VolumeChart() { dataKey={coinName} dot={false} name={coinName.toString()} - stroke={getTokenColor(coinName.toString())} + stroke={getTokenHex(coinName.toString())} key={'open-i-rate-line-' + i} /> ); diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 69ae99f..ce0dc3e 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -12,7 +12,7 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -26,9 +26,7 @@ import { yaxisFormatter, xAxisFormatter, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; - -import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; +import { getTokenHex } from '../../../constants/tokens'; import { cumulative_usd_volume, daily_usd_volume, @@ -36,7 +34,6 @@ import { daily_usd_volume_by_crossed, daily_usd_volume_by_user, } from '../../../constants/api'; -import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [ cumulative_usd_volume, @@ -47,12 +44,10 @@ const REQUESTS = [ ]; export default function RetailVolumeChart() { - const [isMobile] = useIsMobile(); - + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coinKeys, setCoinKeys] = useState([]); const [dataCumulativeUsdVolume, loadingCumulativeUsdVolume, errorCumulativeUsdVolume] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -112,7 +107,6 @@ export default function RetailVolumeChart() { type FormattedVolumeData = any[]; //{ time: string, all: number, [coin: string]: number }; const formatVolumeByCoins = ( - CoinsSelected: string[], dataDailyUsdVolumeByCoin: VolumeData[], formattedCumulativeUsdVolume: { [key: string]: number }, formattedDailyVolumeByTime: { [key: string]: number } @@ -126,21 +120,25 @@ export default function RetailVolumeChart() { temp[data.time].all += data.daily_usd_volume; } - const selectedCoinData = (obj: { [coin: string]: number }) => { - const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) && coin !== "all"); - const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { + const sortedEntries = Object.entries(obj).sort( + ([, aVolume], [, bVolume]) => bVolume - aVolume + ); + const top10Entries = sortedEntries.slice(0, 10); + const otherEntries = sortedEntries.slice(10); + const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(selectedEntries), + ...Object.fromEntries(top10Entries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const selectedVolumes = selectedCoinData(volumes); + const top10Volumes = sortAndSliceTop10(volumes); return { time: new Date(time), - ...selectedVolumes, + ...top10Volumes, cumulative: formattedCumulativeUsdVolume[time as any], all: formattedDailyVolumeByTime[time as any], unit: '$', @@ -150,10 +148,20 @@ export default function RetailVolumeChart() { return result; }; - const extractUniqueCoins = (formattedVolumeData: VolumeData[]): string[] => { + const extractUniqueCoins = (formattedVolumeData: FormattedVolumeData[]): string[] => { const coinSet = new Set(); for (const data of formattedVolumeData) { - coinSet.add(data.coin); + Object.keys(data).forEach((coin) => { + if ( + coin !== 'all' && + coin !== 'cumulative' && + coin !== 'time' && + coin !== 'other' && + coin !== 'unit' + ) { + coinSet.add(coin); + } + }); } const coinsArray = Array.from(coinSet); if (coinsArray.includes('Other')) { @@ -201,11 +209,10 @@ export default function RetailVolumeChart() { return result; }; - const formatData = (CoinsSelected: string[]) => { + const formatData = () => { const formattedCumulativeVolumeByTime = formatCumulativeVolumeByTime(dataCumulativeUsdVolume); const formattedDailyVolumeByTime = formatDailyVolumeByTime(dataDailyUsdVolume); const formattedVolumeByCoins = formatVolumeByCoins( - CoinsSelected, dataDailyUsdVolumeByCoin, formattedCumulativeVolumeByTime, formattedDailyVolumeByTime @@ -215,7 +222,7 @@ export default function RetailVolumeChart() { formattedCumulativeVolumeByTime, formattedDailyVolumeByTime ); - setCoinKeys(extractUniqueCoins(dataDailyUsdVolumeByCoin)); + setCoinKeys(extractUniqueCoins(formattedVolumeByCoins)); setFormattedDataCoins(formattedVolumeByCoins); setFormattedDataMargin(formattedVolumeByCrossed); }; @@ -237,12 +244,10 @@ export default function RetailVolumeChart() { useEffect(() => { if (!loading || error) { - formatData(coinsSelected); + formatData(); } }, [loading, error]); - const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); - return ( {dataMode === 'COINS' && ( <> - {coinsSelected.map((coinName, i) => { + {coinKeys.map((coinName, i) => { return ( diff --git a/components/home/charts/trader-profit.tsx b/components/home/charts/trader-profit.tsx index 11d6687..8f6c60e 100644 --- a/components/home/charts/trader-profit.tsx +++ b/components/home/charts/trader-profit.tsx @@ -23,12 +23,11 @@ import { tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { useIsMobile } from '@/hooks/isMobile'; const REQUESTS = [cumulative_user_pnl, user_pnl]; export default function TradersProfitLossChart() { - const [isMobile] = useIsMobile(); + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [data, setData] = useState(null); const [dataCumulativeUserPNL, loadingCumulativeUserPNL, errorCumulativeUserPNL] = useRequest( @@ -102,12 +101,7 @@ export default function TradersProfitLossChart() { }, [loading, error]); return ( - + diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index b8f4768..4ef7e44 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -12,9 +12,7 @@ import { import { useEffect, useState } from 'react'; import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; - -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN } from '../../../constants'; import { tooltipFormatter, @@ -23,9 +21,7 @@ import { yaxisFormatterNumber, yaxisFormatterPercent, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; - -import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; +import { getTokenHex } from '../../../constants/tokens'; import { cumulative_new_users, daily_unique_users, @@ -72,8 +68,7 @@ type TempGroupedTradeData = { const REQUESTS = [cumulative_new_users, daily_unique_users, daily_unique_users_by_coin]; export default function UniqueUsers() { - const [isMobile] = useIsMobile(); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [formattedData, setFormattedData] = useState([]); const [coinKeys, setCoinKeys] = useState([]); @@ -96,7 +91,6 @@ export default function UniqueUsers() { const error = errorCumulativeNewUsers || errorDailyUniqueUsers || errorDailyUniqueUsersByCoin; const formatTradesByCoinAndTime = ( - CoinsSelected: string[], dataDailyUniqueUsersByCoin: DailyUniqueUsersByCoin[], uniqueUserTradeData: UniqueUserTradeData[], dataCumulativeNewUsers: CumulativeNewUsersData[] @@ -129,12 +123,15 @@ export default function UniqueUsers() { } ); - const selectedCoinData = (obj: { [coin: string]: number }) => { - const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); - const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { + const sortedEntries = Object.entries(obj).sort( + ([, aVolume], [, bVolume]) => bVolume - aVolume + ); + const top10Entries = sortedEntries.slice(0, 10); + const otherEntries = sortedEntries.slice(10); const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(selectedEntries), + ...Object.fromEntries(top10Entries), Other: otherVolume, }; }; @@ -142,50 +139,63 @@ export default function UniqueUsers() { return Object.values(temp).map(({ time, coins, ...rest }) => { return { time: new Date(time), - ...selectedCoinData(coins), + ...sortAndSliceTop10(coins), ...rest, unit: '%', }; }); }; - const extractUniqueCoins = (CoinData: any): string[] => { + const extractUniqueCoins = (formattedVolumeData: GroupedTradeData[]): string[] => { const coinSet = new Set(); - for (const data of CoinData) { - coinSet.add(data.coin); + for (const data of formattedVolumeData) { + Object.keys(data).forEach((coin) => { + if ( + coin !== 'all' && + coin !== 'cumulative' && + coin !== 'time' && + coin !== 'other' && + coin !== 'unit' && + coin !== 'daily_unique_users' && + coin !== 'cumulative_unique_users' && + !coin.includes('daily_unique_users') + ) { + coinSet.add(coin); + } + }); } const coinsArray = Array.from(coinSet); + if (coinsArray.includes('Other')) { + const index = coinsArray.indexOf('Other'); + coinsArray.splice(index, 1); + coinsArray.push('Other'); + } return coinsArray; }; - const formatData = (CoinsSelector: string[]) => { + const formatData = () => { const formattedData = formatTradesByCoinAndTime( - CoinsSelector, dataDailyUniqueUsersByCoin, dataDailyUniqueUsers, dataCumulativeNewUsers ); - const formattedUniqueCoinKeys = extractUniqueCoins(dataDailyUniqueUsersByCoin); + const formattedUniqueCoinKeys = extractUniqueCoins(formattedData); setFormattedData(formattedData); setCoinKeys(formattedUniqueCoinKeys); }; useEffect(() => { if (!loading && !error) { - formatData(coinsSelected); + formatData(); } }, [loading]); - const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); - return ( @@ -229,7 +239,7 @@ export default function UniqueUsers() { }} /> - {coinsSelected.map((coinName, i) => { + {coinKeys.map((coinName, i) => { return ( diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index 28e990b..3f76a84 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -13,9 +13,7 @@ import { import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; - -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, @@ -29,9 +27,7 @@ import { yaxisFormatterNumber, xAxisFormatter, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; - -import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; +import { getTokenHex } from '../../../constants/tokens'; import { cumulative_trades, daily_trades, @@ -49,8 +45,7 @@ const REQUESTS = [ ]; export default function VolumeChart() { - const [isMobile] = useIsMobile(); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'USER'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); @@ -113,7 +108,6 @@ export default function VolumeChart() { type FormattedCoinTradesData = any[]; //{ time: string, all: number, [coin: string]: number }; const formatDailyTradesByCoins = ( - CoinsSelected: string[], dataDailyTradesByCoin: { coin: string; daily_trades: number; time: string }[], formattedCumulativeTradesByTime: { [key: string]: number } ): FormattedCoinTradesData[] => { @@ -126,18 +120,22 @@ export default function VolumeChart() { temp[data.time].all += data.daily_trades; } - const selectedCoinData = (obj: { [coin: string]: number }) => { - const selectedEntries = Object.entries(obj).filter(([coin]) => CoinsSelected.includes(coin) || coin==="all"); - const otherEntries = Object.entries(obj).filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const sortAndSliceTop10 = (obj: { [coin: string]: number }) => { + const sortedEntries = Object.entries(obj).sort( + ([, aVolume], [, bVolume]) => bVolume - aVolume + ); + const top10Entries = sortedEntries.slice(0, 10); + const otherEntries = sortedEntries.slice(10); + const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); return { - ...Object.fromEntries(selectedEntries), + ...Object.fromEntries(top10Entries), Other: otherVolume, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { - const top10Volumes = selectedCoinData(volumes); + const top10Volumes = sortAndSliceTop10(volumes); return { time: new Date(time), ...top10Volumes, @@ -148,12 +146,21 @@ export default function VolumeChart() { return result; }; - const extractUniqueCoins = (CoinData: any): string[] => { + const extractUniqueCoins = (formattedCoinTradesData: FormattedCoinTradesData[]): string[] => { const coinSet = new Set(); - for (const data of CoinData) { - coinSet.add(data.coin); + for (const data of formattedCoinTradesData) { + Object.keys(data).forEach((coin) => { + if (coin !== 'all' && coin !== 'cumulative' && coin !== 'time' && coin !== 'unit') { + coinSet.add(coin); + } + }); } const coinsArray = Array.from(coinSet); + if (coinsArray.includes('Other')) { + const index = coinsArray.indexOf('Other'); + coinsArray.splice(index, 1); + coinsArray.push('Other'); + } return coinsArray; }; @@ -197,10 +204,9 @@ export default function VolumeChart() { return Object.values(groupedByTime); }; - const formatData = (CoinsSelected: string[]) => { + const formatData = () => { const formattedCumulativeTradesByTime = formatTradesByTime(dataCumulativeTrades); const formattedTradesByCoins = formatDailyTradesByCoins( - CoinsSelected, dataDailyTradesByCoin, formattedCumulativeTradesByTime ); @@ -209,7 +215,7 @@ export default function VolumeChart() { formattedCumulativeTradesByTime ); setMaxAllValueUser(maxAllValueUser); - setCoinKeys(extractUniqueCoins(dataDailyTradesByCoin)); + setCoinKeys(extractUniqueCoins(formattedTradesByCoins)); setFormattedDataCoins(formattedTradesByCoins); setFormattedDataMarin(formattedTradesByMargin); }; @@ -231,13 +237,10 @@ export default function VolumeChart() { useEffect(() => { if (!loading && !error) { - formatData(coinsSelected); + formatData(); } }, [loading, dataMode]); - - const coinSelectors = createCoinSelectorsWithFormatArg(coinKeys, coinsSelected, setCoinsSelected, formatData); - return ( - {coinsSelected.map((coinName, i) => { + {coinKeys.map((coinName, i) => { return ( diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index 52dd4b0..ad286c2 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -11,10 +11,8 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useIsMobile } from '@/hooks/isMobile'; - import { Box, Text, useMediaQuery } from '@chakra-ui/react'; -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import ChartWrapper from '../../common/chartWrapper'; import { BRIGHT_GREEN, CHART_HEIGHT, YAXIS_WIDTH } from '../../../constants'; import { yaxisFormatter, @@ -22,17 +20,14 @@ import { tooltipFormatterCurrency, tooltipLabelFormatter, } from '../../../helpers'; -import { createCoinSelectorsWithFormatArg } from "../../../helpers/utils"; - import { total_volume } from '../../../constants/api'; -import { getTokenColor, initialTokensSelectedWithOther } from '@/constants/tokens'; +import { getTokenHex } from '@/constants/tokens'; const REQUESTS = [total_volume]; export default function TotalVolumeChart() { - const [isMobile] = useIsMobile(); + const [isMobile] = useMediaQuery('(max-width: 700px)'); const [formattedData, setFormattedData] = useState([]); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coins, setCoins] = useState([]); const [dataTotalVolume, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -51,9 +46,9 @@ export default function TotalVolumeChart() { Other: number; } - const makeFormattedData = (CoinsSelected: string[], dataTotalVolume: TotalVolume[]): [MergedData[], string[]] => { + const makeFormattedData = (dataTotalVolume: TotalVolume[]): [MergedData[], string[]] => { const map = new Map(); - const uniqueCoins = new Set(); + const uniqueTopCoins = new Set(); let cumulative = 0; dataTotalVolume.forEach((item: TotalVolume) => { @@ -83,40 +78,43 @@ export default function TotalVolumeChart() { key !== 'total' && key !== 'cumulative' && key !== 'other' && - key !== 'unit' && - key !== 'Other' + key !== 'unit' ); - const otherCoins = coinEntries.filter(([coin]) => (!(CoinsSelected.includes(coin))) && (coin !== "all")); + const sortedCoinEntries = coinEntries.sort( + (a, b) => Math.abs(Number(b[1])) - Math.abs(Number(a[1])) + ); + const topCoins = sortedCoinEntries.slice(0, 10).map(([coin]) => coin); + const otherCoins = sortedCoinEntries.slice(10); - coinEntries.forEach(([coin]) => uniqueCoins.add(coin)); + topCoins.forEach((coin) => uniqueTopCoins.add(coin)); let otherTotal = 0; - otherCoins.forEach(([_, value]) => { + otherCoins.forEach(([coin, value]) => { otherTotal += value; + delete entry[coin]; }); entry.Other = otherTotal; }); const result = Array.from(map.values()); - return [result, Array.from(uniqueCoins)]; + uniqueTopCoins.add('Other'); + return [result, Array.from(uniqueTopCoins)]; }; - const formatData = (CoinsSelected: string[]) => { - const [newFormattedData, coins] = makeFormattedData(CoinsSelected, dataTotalVolume); + const formatData = () => { + const [newFormattedData, coins] = makeFormattedData(dataTotalVolume); setCoins(coins); setFormattedData(newFormattedData); }; useEffect(() => { if (!loading && !error) { - formatData(coinsSelected); + formatData(); } }, [loading, error]); - const coinSelectors = createCoinSelectorsWithFormatArg(coins, coinsSelected, setCoinsSelected, formatData); - return ( - + @@ -144,7 +142,7 @@ export default function TotalVolumeChart() { tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} /> - {coinsSelected.map((coin, i) => { + {coins.map((coin, i) => { return ( diff --git a/constants/tokens.ts b/constants/tokens.ts index 8da4f1a..d42bcc4 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -1,18 +1,37 @@ -import * as CryptoJS from 'crypto-js'; +const TOKEN_COLORS: any = { + BNB: '#ebc509', + BTC: '#F2A900', + DOGE: '#cb9800', + ETH: '#8A94B1', + INJ: '#0386FA', + KPEPE: '#509844', + MATIC: '#8C35D5', + Other: '#BBBAC6', + SOL: '#C867F0', + AVAX: '#e74242', + LTC: '#CCCCCC', + ARB: '#FCA100', + LINK: '#81D2FD', + APE: '#4087BE', + ATOM: '#FCA100', + CFX: '#F3654E', + CRV: '#850087', + DYDX: '#BE586C', + FTM: '#568EC0', + GMX: '#59C782', + LDO: '#DB6ED7', + OP: '#7F0182', + RNDR: '#FFA300', + SNX: '#498548', + STX: '#578374', + SUI: '#6A807A', +}; - -export const initialTokensSelected = ['ETH', 'BTC', 'ARB', 'APE', 'ATOM', 'AVAX', 'BNB', 'COMP', 'CRV', 'DOGE']; -export const initialTokensSelectedWithOther = ['ETH', 'BTC', 'ARB', 'APE', 'ATOM', 'AVAX', 'BNB', 'COMP', 'CRV', 'DOGE','Other']; - -export function getTokenColor(inputString: string): string { - if (inputString == "Other") { - return "pink"; - } - // Use the CryptoJS library to get the MD5 hash of the string - let hash = CryptoJS.MD5("col"+inputString); - - // Convert the hash into a hex string - let color = hash.toString(CryptoJS.enc.Hex).substr(0, 6); - - return "#" + color; -} +export const getTokenHex = (token: string) => { + const symbol = token.toUpperCase(); + if (TOKEN_COLORS[symbol]) { + return TOKEN_COLORS[symbol]; + } else { + return 'pink'; + } +}; diff --git a/helpers/utils.ts b/helpers/utils.ts deleted file mode 100644 index 5228178..0000000 --- a/helpers/utils.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { SetStateAction } from 'react'; -import { CoinSelector } from '../components/common/chartWrapper'; - -const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { - if (a.isChecked !== b.isChecked) { - return a.isChecked ? -1 : 1; - } - return a.name.localeCompare(b.name); - }; - - -export const createCoinSelectors = (coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, formatData: () => any) => { - return coinKeys.map((coinKey: string) => { - return { - name: coinKey, - event: () => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(); - setCoinsSelected(newCoinsSelected); - }, - isChecked: coinsSelected.includes(coinKey), - }; - }).sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); -} - -export const createCoinSelectorsWithFormatArg = (coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, formatData: (arg: string[]) => any) => { - return coinKeys.map((coinKey: string) => { - return { - name: coinKey, - event: () => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } - formatData(newCoinsSelected); - setCoinsSelected(newCoinsSelected); - }, - isChecked: coinsSelected.includes(coinKey), - }; - }).sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); -} \ No newline at end of file diff --git a/hooks/isMobile.ts b/hooks/isMobile.ts deleted file mode 100644 index f9cdffa..0000000 --- a/hooks/isMobile.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useEffect, useState } from 'react'; - -export function useIsMobile() { - if (typeof window === "undefined") { - return [false]; - } - let [isMobile, setIsMobile] = useState(window.innerWidth < 700); - useEffect(() => { - setIsMobile(window.innerWidth < 700); - }, [window.innerWidth]); - - return [isMobile]; -} diff --git a/package-lock.json b/package-lock.json index 48aec2d..ed29c88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,13 +14,11 @@ "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@svgr/webpack": "^8.0.1", - "@types/crypto-js": "^4.1.1", "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-dom": "18.2.4", "@types/strftime": "^0.9.4", "axios": "^1.4.0", - "crypto-js": "^4.1.1", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "ethers": "^5.7.2", @@ -4402,11 +4400,6 @@ "integrity": "sha512-TnhDAntcJthcCMrR3OAKAUjgHyQgoms1yaBJepGv+BtXi8PLf8aX2L/NMCfofRTpVqW0bLklpGTsuqmUSCR2Uw==", "dev": true }, - "node_modules/@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==" - }, "node_modules/@types/d3-array": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", @@ -5253,11 +5246,6 @@ "node": ">= 8" } }, - "node_modules/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" - }, "node_modules/css-box-model": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", @@ -12401,11 +12389,6 @@ "integrity": "sha512-TnhDAntcJthcCMrR3OAKAUjgHyQgoms1yaBJepGv+BtXi8PLf8aX2L/NMCfofRTpVqW0bLklpGTsuqmUSCR2Uw==", "dev": true }, - "@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==" - }, "@types/d3-array": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", @@ -13045,11 +13028,6 @@ "which": "^2.0.1" } }, - "crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" - }, "css-box-model": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", diff --git a/package.json b/package.json index 5f9ddb2..e056236 100644 --- a/package.json +++ b/package.json @@ -63,13 +63,11 @@ "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@svgr/webpack": "^8.0.1", - "@types/crypto-js": "^4.1.1", "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-dom": "18.2.4", "@types/strftime": "^0.9.4", "axios": "^1.4.0", - "crypto-js": "^4.1.1", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "ethers": "^5.7.2", From 29de0417a52a9cf84daf88cfc3d96efec768c18a Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Fri, 4 Aug 2023 04:43:50 +0900 Subject: [PATCH 38/59] Misc clean up and fixes (#18) --- components/common/chartWrapper/index.tsx | 169 ++++++++++-------- components/home/charts/cumulative-inflow.tsx | 13 +- components/home/charts/cumulative-users.tsx | 18 +- components/home/charts/fees.tsx | 13 +- components/home/charts/funding-rate.tsx | 24 +-- components/home/charts/hlp.tsx | 37 ++-- components/home/charts/liquidator.tsx | 21 +-- components/home/charts/liquidity.tsx | 11 +- components/home/charts/open-interest.tsx | 6 +- components/home/charts/retail-volume.tsx | 21 +-- components/home/charts/trader-profit.tsx | 15 +- components/home/charts/unique-users-coin.tsx | 18 +- components/home/charts/volume-num-trades.tsx | 29 +-- components/home/charts/volume-total.tsx | 29 +-- components/home/main/index.tsx | 29 ++- components/home/tables/largest-users.tsx | 11 +- .../home/tables/liquidated-notional-user.tsx | 15 +- components/home/tables/user-deposits.tsx | 11 +- components/home/tables/user-trade-count.tsx | 11 +- constants/tokens.ts | 5 +- helpers/utils.ts | 2 +- hooks/useIsMobile.ts | 16 ++ 22 files changed, 234 insertions(+), 290 deletions(-) create mode 100644 hooks/useIsMobile.ts diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index 02966d5..d0effa3 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -1,3 +1,4 @@ +import { useIsMobile } from '@/hooks/useIsMobile'; import { ChevronDownIcon } from '@chakra-ui/icons'; import { Box, @@ -10,7 +11,6 @@ import { MenuList, MenuItemOption, MenuOptionGroup, - useMediaQuery, Grid, } from '@chakra-ui/react'; @@ -32,9 +32,19 @@ const Loader = () => ( ); -function ChartWrapper(props: any) { - let isMobile = props.isMobile; - const { title, loading, controls, zIndex, coinSelectors } = props; +type Props = { + title: string; + loading: boolean; + controls?: { + toggles: Toggle[]; + }; + zIndex?: number; + coinSelectors?: CoinSelector[]; + children?: React.ReactNode; +}; + +function ChartWrapper({ title, loading, controls, zIndex, coinSelectors, children }: Props) { + const isMobile = useIsMobile(); const controlButtons = controls && controls.toggles && @@ -52,6 +62,64 @@ function ChartWrapper(props: any) { ); }); + const coinSelectorsMenu = coinSelectors && ( + + + } + variant='primary' + fontSize={'14px'} + size='sm' + > + Select coins + + + coinSelector.isChecked) + .map((coinSelector: CoinSelector) => coinSelector.name)} + > + {coinSelectors.map((coinSelector: CoinSelector, index: number) => { + return ( + coinSelector.event()} + isChecked={coinSelector.isChecked} + > + {coinSelector.name} + + ); + })} + + + + + ); + return ( {title} - {controls && ( - - {isMobile ? ( - - {controlButtons} - - ) : ( + + {isMobile ? ( + + {controlButtons} + {coinSelectorsMenu} + + ) : ( + <> {controlButtons} - )} - - )} - {coinSelectors && ( - - - } - variant='primary' - fontSize={'14px'} - size='sm' - > - Select coins - - - coinSelector.isChecked) - .map((coinSelector: CoinSelector) => coinSelector.name)} - > - {coinSelectors.map((coinSelector: CoinSelector, index: number) => { - return ( - coinSelector.event()} - isChecked={coinSelector.isChecked} - > - {coinSelector.name} - - ); - })} - - - - - )} + {coinSelectorsMenu} + + )} + {loading && } - {props.children} + {children} ); diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 00dbfcf..5357cb3 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -12,7 +12,6 @@ import { } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { useMediaQuery } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, GREEN, RED } from '../../../constants'; import { @@ -25,9 +24,7 @@ import { daily_inflow, cumulative_inflow } from '../../../constants/api'; const REQUESTS = [daily_inflow, cumulative_inflow]; -export default function CumulativeInflow(props: any) { - const isMobile = props.isMobile; - +export default function CumulativeInflow() { const [formattedData, setFormattedData] = useState([]); const [dataDailyInflow, loadingDailyInflow, errorDailyInflow] = useRequest( REQUESTS[0], @@ -99,7 +96,7 @@ export default function CumulativeInflow(props: any) { }, [loading, errorDailyInflow]); return ( - + @@ -107,7 +104,7 @@ export default function CumulativeInflow(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> ([]); const [dataCumulativeNewUsers, loadingCumulativeNewUsers, errorCumulativeNewUsers] = useRequest( @@ -74,12 +71,7 @@ export default function CumulativeUsers(props: any) { }, [loading, error]); return ( - + @@ -87,7 +79,7 @@ export default function CumulativeUsers(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> ([]); const [dailyFeesAccrued, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -69,7 +66,7 @@ export default function Fees(props: any) { }, [loading, error]); return ( - + @@ -77,7 +74,7 @@ export default function Fees(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> ([]); const [formattedData, setFormattedData] = useState([]); const [dataFundingRate, loadingFundingRate, errorFundingRate] = useRequest( @@ -118,13 +115,7 @@ export default function FundingRate(props: any) { const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); return ( - + @@ -132,15 +123,10 @@ export default function FundingRate(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> - + ): [GroupedData[], string[]] => { + const makeFormattedData = ( + hlpPositions: HlpPosition[], + hlpPnL: Map + ): [GroupedData[], string[]] => { const map = new Map(); const uniqueTopCoins = new Set(); @@ -209,7 +206,7 @@ export default function Hlp(props: any) { active: dataMode === 'HEDGED', }, { - text: 'Net notional position', + text: 'Net position', event: () => setDataMode('NET'), active: dataMode === 'NET', }, @@ -238,13 +235,7 @@ export default function Hlp(props: any) { }, [loading, error]); return ( - + @@ -252,15 +243,10 @@ export default function Hlp(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> - + ); -} \ No newline at end of file +} diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index 19f2346..92a0481 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -300,22 +300,15 @@ export default function LiquidatorChart(props: any) { return [-1 * Math.abs(maxCumulativePnl) * 1.1, Math.abs(maxCumulativePnl) * 1.1]; }; - const coinSelectors = createCoinSelectors( - coinKeys, - coinsSelected, - setCoinsSelected, - formatData - ); + const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); return ( @@ -324,7 +317,7 @@ export default function LiquidatorChart(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> @@ -238,15 +236,10 @@ export default function Liquidity(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> - + + @@ -134,14 +134,14 @@ export default function VolumeChart(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> + @@ -114,7 +109,7 @@ export default function TradersProfitLossChart(props: any) { dataKey='timestamp' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> (initialTokensSelectedWithOther); const [formattedData, setFormattedData] = useState([]); @@ -179,20 +178,13 @@ export default function UniqueUsers(props: any) { } }, [loading]); - const coinSelectors = createCoinSelectors( - coinKeys, - coinsSelected, - setCoinsSelected, - formatData - ); + const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); return ( @@ -202,7 +194,7 @@ export default function UniqueUsers(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> (initialTokensSelectedWithOther); const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'USER'>('COINS'); @@ -238,28 +236,15 @@ export default function VolumeChart(props: any) { } }, [loading, dataMode]); - const coinSelectors = createCoinSelectors( - coinKeys, - coinsSelected, - setCoinsSelected, - formatData - ); + const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); return ( @@ -287,7 +272,7 @@ export default function VolumeChart(props: any) { domain={['0', maxAllValueUser * 1.1]} tickFormatter={yaxisFormatterNumber} width={YAXIS_WIDTH} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} unit={''} /> ([]); const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coins, setCoins] = useState([]); @@ -118,21 +116,10 @@ export default function TotalVolumeChart(props: any) { } }, [loading, error]); - const coinSelectors = createCoinSelectors( - coins, - coinsSelected, - setCoinsSelected, - formatData - ); + const coinSelectors = createCoinSelectors(coins, coinsSelected, setCoinsSelected, formatData); return ( - + @@ -140,7 +127,7 @@ export default function TotalVolumeChart(props: any) { dataKey='time' tickFormatter={xAxisFormatter} minTickGap={30} - tick={{ fill: '#f9f9f9', fontSize: isMobile ? 14 : 15 }} + tick={{ fill: '#f9f9f9' }} tickMargin={10} /> {coinsSelected.map((coin, i) => { diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index b4557ba..d937a13 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -1,7 +1,6 @@ 'use client'; import React from 'react'; import { Container, Box, Text, Grid, Flex } from '@chakra-ui/react'; -import * as S from './styles'; import TopStats from '../charts/top-stats'; import RetailVolumeChart from '../charts/retail-volume'; import VolumeNumTrades from '../charts/volume-num-trades'; @@ -20,12 +19,8 @@ import Liquidity from '../charts/liquidity'; import HlpExposure from '../charts/hlp'; import TotalVolumeChart from '../charts/volume-total'; import UniqueUsers from '../charts/unique-users-coin'; -import { useEffect, useState } from 'react'; -import { useMediaQuery } from '@chakra-ui/react'; const Main = () => { - const isMobile = useMediaQuery('(max-width: 700px)'); - return ( { - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/components/home/tables/largest-users.tsx b/components/home/tables/largest-users.tsx index 0688391..093e448 100644 --- a/components/home/tables/largest-users.tsx +++ b/components/home/tables/largest-users.tsx @@ -71,7 +71,14 @@ export default function TableComponent() { ); return ( - + Largest Users By USD Volume @@ -142,6 +149,6 @@ export default function TableComponent() { Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByUsdVolume.length} - + ); } diff --git a/components/home/tables/liquidated-notional-user.tsx b/components/home/tables/liquidated-notional-user.tsx index 7c777ba..d4718cf 100644 --- a/components/home/tables/liquidated-notional-user.tsx +++ b/components/home/tables/liquidated-notional-user.tsx @@ -17,7 +17,6 @@ import { import { largest_liquidated_notional_by_user } from '../../../constants/api'; import { formatNumberWithOptions } from '../../../helpers/index'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper from '../../common/chartWrapper'; import { formatAddress } from '../../../utils/formatting'; const REQUESTS = [largest_liquidated_notional_by_user]; @@ -56,12 +55,11 @@ export default function TableComponent() { page, canPreviousPage, canNextPage, - pageOptions, pageCount, gotoPage, nextPage, previousPage, - state: { pageIndex, pageSize }, + state: { pageIndex }, } = useTable( { columns, @@ -72,7 +70,14 @@ export default function TableComponent() { ); return ( - + Largest Liquidated Users By USD @@ -143,6 +148,6 @@ export default function TableComponent() { Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestLiquidatedNotional.length} - + ); } diff --git a/components/home/tables/user-deposits.tsx b/components/home/tables/user-deposits.tsx index 721a3f7..c268eef 100644 --- a/components/home/tables/user-deposits.tsx +++ b/components/home/tables/user-deposits.tsx @@ -72,7 +72,14 @@ export default function TableComponent() { ); return ( - + Largest User Deposits By USD Value @@ -143,6 +150,6 @@ export default function TableComponent() { Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByDeposits.length} - + ); } diff --git a/components/home/tables/user-trade-count.tsx b/components/home/tables/user-trade-count.tsx index d044da8..0c55685 100644 --- a/components/home/tables/user-trade-count.tsx +++ b/components/home/tables/user-trade-count.tsx @@ -71,7 +71,14 @@ export default function TableComponent() { ); return ( - + Largest Trade Count by Users @@ -142,6 +149,6 @@ export default function TableComponent() { Page: {pageIndex + 1} / {pageCount} | Results: {dataLargestUsersByTradeCount.length} - + ); } diff --git a/constants/tokens.ts b/constants/tokens.ts index a5564b2..af9d239 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -12,10 +12,7 @@ export const initialTokensSelected = [ 'DOGE', 'ETH', ]; -export const initialTokensSelectedWithOther = [ - ...initialTokensSelected, - 'Other', -]; +export const initialTokensSelectedWithOther = [...initialTokensSelected, 'Other']; export function getTokenColor(token: string): string { if (token == 'Other') { diff --git a/helpers/utils.ts b/helpers/utils.ts index 5101b8d..792900b 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -39,4 +39,4 @@ export const createCoinSelectors = ( }; }) .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); -}; \ No newline at end of file +}; diff --git a/hooks/useIsMobile.ts b/hooks/useIsMobile.ts new file mode 100644 index 0000000..2081d7f --- /dev/null +++ b/hooks/useIsMobile.ts @@ -0,0 +1,16 @@ +import { useEffect, useState } from 'react'; + +export function useIsMobile() { + const [isMobile, setIsMobile] = useState(true); + const handleWindowResize = () => { + setIsMobile(window.innerWidth < 700); + }; + + useEffect(() => { + handleWindowResize(); + window.addEventListener('resize', handleWindowResize); + return () => window.removeEventListener('resize', handleWindowResize); + }, []); + + return isMobile; +} From b8d83a939eb1396cd91004a48f63637c8d8bebeb Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Fri, 11 Aug 2023 00:30:21 +0900 Subject: [PATCH 39/59] Fixing slippage chart and misc clean up (#19) --- components/common/chartWrapper/index.tsx | 71 +++++++------------- components/home/charts/cumulative-inflow.tsx | 2 +- components/home/charts/cumulative-users.tsx | 2 +- components/home/charts/fees.tsx | 2 +- components/home/charts/funding-rate.tsx | 2 +- components/home/charts/hlp.tsx | 7 +- components/home/charts/liquidator.tsx | 2 +- components/home/charts/liquidity.tsx | 62 ++++++----------- components/home/charts/open-interest.tsx | 2 +- components/home/charts/retail-volume.tsx | 2 +- components/home/charts/trader-profit.tsx | 2 +- components/home/charts/unique-users-coin.tsx | 2 +- components/home/charts/volume-num-trades.tsx | 2 +- components/home/tables/user-trade-count.tsx | 3 +- constants/tokens.ts | 1 - helpers/index.ts | 11 +-- helpers/utils.ts | 1 - hooks/useIsMobile.ts | 2 +- styles/theme.ts | 3 +- 19 files changed, 70 insertions(+), 111 deletions(-) diff --git a/components/common/chartWrapper/index.tsx b/components/common/chartWrapper/index.tsx index d0effa3..c357876 100644 --- a/components/common/chartWrapper/index.tsx +++ b/components/common/chartWrapper/index.tsx @@ -63,12 +63,7 @@ function ChartWrapper({ title, loading, controls, zIndex, coinSelectors, childre }); const coinSelectorsMenu = coinSelectors && ( - + ); + const menu = ( + + + {isMobile && controls ? ( + controlButtons + ) : ( + {controlButtons} + )} + {coinSelectorsMenu} + + + ); + return ( - + - - - - {title} - - - {isMobile ? ( - - {controlButtons} - {coinSelectorsMenu} - - ) : ( - <> - {controlButtons} - {coinSelectorsMenu} - - )} - - - + + + {title} + + {menu} + {loading && } {children} diff --git a/components/home/charts/cumulative-inflow.tsx b/components/home/charts/cumulative-inflow.tsx index 5357cb3..4416476 100644 --- a/components/home/charts/cumulative-inflow.tsx +++ b/components/home/charts/cumulative-inflow.tsx @@ -97,7 +97,7 @@ export default function CumulativeInflow() { return ( - + - + - + - + - + {dataMode === 'HEDGED' && ( - Hedged PNL over time. Hedge the previous day's position and add to today's - PNL. + Hedge each day's returns by holding the previous day's ending positions for + each asset. )} diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index 92a0481..16d2643 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -310,7 +310,7 @@ export default function LiquidatorChart(props: any) { zIndex={7} coinSelectors={dataMode === 'COINS' ? coinSelectors : undefined} > - + ([]); const [formattedData1000, setFormattedData1000] = useState([]); - const [formattedData3000, setFormattedData3000] = useState([]); const [formattedData10000, setFormattedData10000] = useState([]); const [coinKeys, setCoinKeys] = useState([]); const [coinKeys0, setCoinKeys0] = useState([]); const [coinKeys1000, setCoinKeys1000] = useState([]); - const [coinKeys3000, setCoinKeys3000] = useState([]); const [coinKeys10000, setCoinKeys10000] = useState([]); - const [dataMode, setDataMode] = useState<'0' | '1000' | '3000' | '10000'>('0'); + const [dataMode, setDataMode] = useState<'0' | '1000' | '10000'>('0'); const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -59,11 +56,6 @@ export default function Liquidity(props: any) { event: () => setDataMode('1000'), active: dataMode === '1000', }, - { - text: '$3k', - event: () => setDataMode('3000'), - active: dataMode === '3000', - }, { text: '$10k', event: () => setDataMode('10000'), @@ -76,7 +68,6 @@ export default function Liquidity(props: any) { [key: string]: { median_slippage_0: number; median_slippage_1000: number; - median_slippage_3000: number; median_slippage_10000: number; time: string; }[]; @@ -85,7 +76,6 @@ export default function Liquidity(props: any) { type OutputData = { median_slippage_0: { time: Date; [key: string]: number | Date | string }[]; median_slippage_1000: { time: Date; [key: string]: number | Date | string }[]; - median_slippage_3000: { time: Date; [key: string]: number | Date | string }[]; median_slippage_10000: { time: Date; [key: string]: number | Date | string }[]; }; @@ -112,10 +102,6 @@ export default function Liquidity(props: any) { string, { time: Date; [key: string]: number | Date | string } >(); - const median_slippage_3000 = new Map< - string, - { time: Date; [key: string]: number | Date | string } - >(); const median_slippage_10000 = new Map< string, { time: Date; [key: string]: number | Date | string } @@ -130,40 +116,42 @@ export default function Liquidity(props: any) { time, median_slippage_0: val_0, median_slippage_1000: val_1000, - median_slippage_3000: val_3000, median_slippage_10000: val_10000, } = record; const map0 = median_slippage_0.get(time) || { time: new Date(time), unit: '%' }; const map1000 = median_slippage_1000.get(time) || { time: new Date(time), unit: '%' }; - const map3000 = median_slippage_3000.get(time) || { time: new Date(time), unit: '%' }; const map10000 = median_slippage_10000.get(time) || { time: new Date(time), unit: '%' }; map0[key] = val_0 * 100; map1000[key] = val_1000 * 100; - map3000[key] = val_3000 * 100; map10000[key] = val_10000 * 100; median_slippage_0.set(time, map0); median_slippage_1000.set(time, map1000); - median_slippage_3000.set(time, map3000); median_slippage_10000.set(time, map10000); }); } + const sortByDate = (a: Date, b: Date) => { + return a.valueOf() - b.valueOf(); + }; + return { - median_slippage_0: Array.from(median_slippage_0.values()), - median_slippage_1000: Array.from(median_slippage_1000.values()), - median_slippage_3000: Array.from(median_slippage_3000.values()), - median_slippage_10000: Array.from(median_slippage_10000.values()), + median_slippage_0: Array.from(median_slippage_0.values()).sort((a, b) => + sortByDate(a.time, b.time) + ), + median_slippage_1000: Array.from(median_slippage_1000.values()).sort((a, b) => + sortByDate(a.time, b.time) + ), + median_slippage_10000: Array.from(median_slippage_10000.values()).sort((a, b) => + sortByDate(a.time, b.time) + ), }; }; const extractUniqueCoins = ( - data: - | OutputData['median_slippage_1000'] - | OutputData['median_slippage_10000'] - | OutputData['median_slippage_3000'] + data: OutputData['median_slippage_1000'] | OutputData['median_slippage_10000'] ): string[] => { const coinSet = new Set(); data.forEach((record) => { @@ -182,16 +170,13 @@ export default function Liquidity(props: any) { const formattedData = transformData(dataLiqudity); setFormattedData0(formattedData.median_slippage_0); setFormattedData1000(formattedData.median_slippage_1000); - setFormattedData3000(formattedData.median_slippage_3000); setFormattedData10000(formattedData.median_slippage_10000); const formattedUniqueCoinKeys0 = extractUniqueCoins(formattedData.median_slippage_0); const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000); - const formattedUniqueCoinKeys3000 = extractUniqueCoins(formattedData.median_slippage_3000); const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000); setCoinKeys0(formattedUniqueCoinKeys0); setCoinKeys1000(formattedUniqueCoinKeys1000); - setCoinKeys3000(formattedUniqueCoinKeys3000); setCoinKeys10000(formattedUniqueCoinKeys10000); }; @@ -206,30 +191,22 @@ export default function Liquidity(props: any) { ? formattedData0 : dataMode === '1000' ? formattedData1000 - : dataMode === '3000' - ? formattedData3000 : formattedData10000; const chartDataCoinKeys = - dataMode === '0' - ? coinKeys0 - : dataMode === '1000' - ? coinKeys1000 - : dataMode === '3000' - ? coinKeys3000 - : coinKeys10000; + dataMode === '0' ? coinKeys0 : dataMode === '1000' ? coinKeys1000 : coinKeys10000; const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); return ( - + ); })} diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index cce4400..3265f0c 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -127,7 +127,7 @@ export default function VolumeChart(props: any) { return ( - + - + - + - + - + - Largest Trade Count by Users + Largest Trade Count By Users diff --git a/constants/tokens.ts b/constants/tokens.ts index af9d239..2d9321a 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -1,7 +1,6 @@ import * as CryptoJS from 'crypto-js'; export const initialTokensSelected = [ - 'APE', 'ARB', 'ATOM', 'AVAX', diff --git a/helpers/index.ts b/helpers/index.ts index bd54990..9abb108 100644 --- a/helpers/index.ts +++ b/helpers/index.ts @@ -63,16 +63,19 @@ interface FormatNumberOpts { export const formatNumberWithOptions = (value: number, opts: FormatNumberOpts = {}): string => { const currency = !!opts.currency; const compact = !!opts.compact; + const sign = value < 0 ? '-' : ''; + const absoluteValue = Math.abs(value); if (currency && !compact) { - return getCurrencyFormatBasedOnValue(value).format(value); + return sign + getCurrencyFormatBasedOnValue(absoluteValue).format(absoluteValue); } const display = compact - ? formatNumberToCompactForm(value) - : getNumberFormatBasedOnValue(value).format(value); + ? formatNumberToCompactForm(absoluteValue) + : getNumberFormatBasedOnValue(absoluteValue).format(absoluteValue); + if (currency) { - return `$${display}`; + return `${sign}$${display}`; } return display; }; diff --git a/helpers/utils.ts b/helpers/utils.ts index 792900b..2dd6abf 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -1,4 +1,3 @@ -import { SetStateAction } from 'react'; import { CoinSelector } from '../components/common/chartWrapper'; const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { diff --git a/hooks/useIsMobile.ts b/hooks/useIsMobile.ts index 2081d7f..793f162 100644 --- a/hooks/useIsMobile.ts +++ b/hooks/useIsMobile.ts @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'; export function useIsMobile() { const [isMobile, setIsMobile] = useState(true); const handleWindowResize = () => { - setIsMobile(window.innerWidth < 700); + setIsMobile(window.innerWidth < 900); }; useEffect(() => { diff --git a/styles/theme.ts b/styles/theme.ts index ced7f11..d7229e8 100644 --- a/styles/theme.ts +++ b/styles/theme.ts @@ -1,5 +1,4 @@ import { extendTheme, ThemeConfig } from '@chakra-ui/react'; -import { createBreakpoints } from '@chakra-ui/theme-tools'; import Button from './components/button'; import Progress from './components/progress'; import Text from './components/text'; @@ -20,7 +19,7 @@ const breakpoints = { sm: '576px', md: '768px', lg: '992px', - xl: '1500px', + xl: '1400px', xxl: '2400px', }; From e76c2de179d55ab5bc8b3117f677fe65e59c66cf Mon Sep 17 00:00:00 2001 From: Ben Razon Date: Thu, 10 Aug 2023 17:17:16 -0400 Subject: [PATCH 40/59] Add mixpanel event for loading stats page --- components/home/main/index.tsx | 11 ++++++++++- package-lock.json | 24 ++++++++++++++++++++++++ package.json | 2 ++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index d937a13..1fc9308 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -1,5 +1,5 @@ 'use client'; -import React from 'react'; +import React, { useEffect } from 'react'; import { Container, Box, Text, Grid, Flex } from '@chakra-ui/react'; import TopStats from '../charts/top-stats'; import RetailVolumeChart from '../charts/retail-volume'; @@ -19,8 +19,17 @@ import Liquidity from '../charts/liquidity'; import HlpExposure from '../charts/hlp'; import TotalVolumeChart from '../charts/volume-total'; import UniqueUsers from '../charts/unique-users-coin'; +import mixpanel from 'mixpanel-browser'; const Main = () => { + useEffect(() => { + if (process.env.NEXT_PUBLIC_MIXPANEL_TOKEN) { + mixpanel.init(process.env.NEXT_PUBLIC_MIXPANEL_TOKEN, { + api_host: "https://metrics.hyperliquid.xyz", + }); + mixpanel.track("Stats Loaded"); + } + }, []); return ( Date: Mon, 28 Aug 2023 15:37:45 -0700 Subject: [PATCH 41/59] Update retail volume description --- components/home/charts/retail-volume.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 3c3697b..f47e08d 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -356,9 +356,9 @@ export default function RetailVolumeChart(props: any) { - {dataMode === 'COINS' && ( - Top 10 Coins grouped daily and remaining coins grouped by Other - )} + + This measures two-sided volume, i.e. each side of a trade is counted once if that side is retail. + ); From 6419e387c3f9a4914ae8d786ad437b1454aa31f8 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:29:05 -0400 Subject: [PATCH 42/59] Fixes and remove liquidator (#22) --- components/home/charts/hlp.tsx | 5 - components/home/charts/liquidator.tsx | 149 ++++--------------- components/home/charts/retail-volume.tsx | 1 + components/home/charts/volume-num-trades.tsx | 6 - components/home/charts/volume-total.tsx | 8 +- components/home/main/index.tsx | 4 +- 6 files changed, 33 insertions(+), 140 deletions(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 7323982..24db7eb 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -345,11 +345,6 @@ export default function Hlp() { - - {dataMode === 'COINS' && ( - Top 10 Coins grouped daily and remaining coins grouped by Other - )} - {dataMode === 'PNL' && PNL over time} diff --git a/components/home/charts/liquidator.tsx b/components/home/charts/liquidator.tsx index 16d2643..2933bc9 100644 --- a/components/home/charts/liquidator.tsx +++ b/components/home/charts/liquidator.tsx @@ -8,20 +8,16 @@ import { ResponsiveContainer, ComposedChart, Line, - Cell, } from 'recharts'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { Box, Text, useMediaQuery } from '@chakra-ui/react'; -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; +import ChartWrapper from '../../common/chartWrapper'; import { CHART_HEIGHT, YAXIS_WIDTH, BRIGHT_GREEN, BRAND_GREEN_2, BRAND_GREEN_3, - GREEN, - RED, } from '../../../constants'; import { tooltipLabelFormatter, @@ -54,7 +50,7 @@ const REQUESTS = [ export default function LiquidatorChart(props: any) { const isMobile = props.isMobile; - const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN' | 'PNL'>('COINS'); + const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); @@ -72,11 +68,6 @@ export default function LiquidatorChart(props: any) { ] = useRequest(REQUESTS[2], [], 'chart_data'); const [dataDailyLiquidatedByCoins, loadingDailyLiquidatedByCoins, errorDailyLiquidatedByCoins] = useRequest(REQUESTS[3], [], 'chart_data'); - const [dataLiquidatorPnl, loadingLiquidatorPnl, errorLiquidatorPnl] = useRequest( - REQUESTS[4], - [], - 'chart_data' - ); const [ dataLiquidatorCumulativePnl, loadingLiquidatorCumulativePnl, @@ -95,14 +86,12 @@ export default function LiquidatorChart(props: any) { loadingDailyLiquidatedTotal || loadingDailyLiquidatedByMargin || loadingDailyLiquidatedByCoins || - loadingLiquidatorPnl || loadingLiquidatorCumulativePnl; const error = errorCumulativeLiquidated || errorDailyUsdVolumeTotal || errorDailyLiquidatedByMargin || errorDailyLiquidatedByCoins || - errorLiquidatorPnl || errorLiquidatorCumulativePnl; type CumulativeLiquidationData = { cumulative: number; time: string }; @@ -116,28 +105,6 @@ export default function LiquidatorChart(props: any) { return result; }; - const formatLiquidatorPnl = ( - dataLiquidatorPnl: any, - dataLiquidatorCumulativePnl: any - ): LiquidatorPnl[] => { - const map = new Map(); - dataLiquidatorPnl.map((item: any) => { - let entry = { - time: new Date(item.time), - pnl: item.total_pnl, - cumulativePnl: 0, - }; - map.set(item.time, entry); - }); - - dataLiquidatorCumulativePnl.map((item: any) => { - let existingEntry = map.get(item.time)!; - existingEntry.cumulativePnl = item.cumulative_pnl; - }); - - return Array.from(map.values()); - }; - type LiquidationData = { time: string; leverage_type: 'Cross' | 'Isolated'; @@ -237,11 +204,6 @@ export default function LiquidatorChart(props: any) { dataDailyLiquidatedByCoins, formattedCumulativeLiquidatedByTime ); - const newFormattedLiquidatorPnl = formatLiquidatorPnl( - dataLiquidatorPnl, - dataLiquidatorCumulativePnl - ); - setFormattedLiquidatorPnl(newFormattedLiquidatorPnl); setCoinKeys(extractUniqueCoins(dataDailyLiquidatedByCoins)); setFormattedDataMargin(formattedVolumeByMargin); setFormattedDataCoins(formattedDailyTradesByCoins); @@ -260,11 +222,6 @@ export default function LiquidatorChart(props: any) { event: () => setDataMode('MARGIN'), active: dataMode === 'MARGIN', }, - { - text: 'Liquidator PnL', - event: () => setDataMode('PNL'), - active: dataMode === 'PNL', - }, ], }; @@ -322,7 +279,7 @@ export default function LiquidatorChart(props: any) { /> )} - {dataMode !== 'PNL' && ( - <> - - - - - )} - {dataMode === 'PNL' && ( - <> - - - - {formattedLiquidatorPnl.map((item: any, i: number) => { - return 0 ? GREEN : RED} />; - })} - - - - )} + + + - - Top 10 Coins grouped daily and remaining coins grouped by Other - ); } diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index f47e08d..2ec7d8f 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -293,6 +293,7 @@ export default function RetailVolumeChart(props: any) { boxShadow: '0px 0px 7px rgb(0 0 0 / 20%)', borderRadius: '26px', color: '#fff', + maxHeight: '500px', }} itemSorter={(item) => { return Number(item.value) * -1; diff --git a/components/home/charts/volume-num-trades.tsx b/components/home/charts/volume-num-trades.tsx index a9d1454..e699631 100644 --- a/components/home/charts/volume-num-trades.tsx +++ b/components/home/charts/volume-num-trades.tsx @@ -9,7 +9,6 @@ import { ComposedChart, Line, } from 'recharts'; -import { Box, Text, useMediaQuery } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; @@ -376,11 +375,6 @@ export default function VolumeChart() { /> - - {dataMode === 'COINS' && ( - Top 10 Coins grouped daily and remaining coins grouped by Other - )} - ); } diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index 0d4d5c0..7425d82 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -12,7 +12,6 @@ import { import { useEffect, useState } from 'react'; import { useRequest } from '@/hooks/useRequest'; -import { Box, Text } from '@chakra-ui/react'; import ChartWrapper from '../../common/chartWrapper'; import { BRIGHT_GREEN, CHART_HEIGHT, YAXIS_WIDTH } from '../../../constants'; import { @@ -185,18 +184,13 @@ export default function TotalVolumeChart() { borderRadius: '26px', maxHeight: '500px', }} + position={{ y: -100 }} itemSorter={(item) => { return Number(item.value) * -1; }} /> - - - Top 10 Coins grouped daily and remaining coins grouped by Other. Volume tracked since - introduction of fees on 6/13/2023. - - ); } diff --git a/components/home/main/index.tsx b/components/home/main/index.tsx index 1fc9308..04f7611 100644 --- a/components/home/main/index.tsx +++ b/components/home/main/index.tsx @@ -25,9 +25,9 @@ const Main = () => { useEffect(() => { if (process.env.NEXT_PUBLIC_MIXPANEL_TOKEN) { mixpanel.init(process.env.NEXT_PUBLIC_MIXPANEL_TOKEN, { - api_host: "https://metrics.hyperliquid.xyz", + api_host: 'https://metrics.hyperliquid.xyz', }); - mixpanel.track("Stats Loaded"); + mixpanel.track('Stats Loaded'); } }, []); return ( From 831063db381d38c38607e1418a0ff63007af7d0f Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Tue, 5 Sep 2023 14:44:22 -0400 Subject: [PATCH 43/59] Add coin selector dropdown for open interest graph & add option to remove "All" from coin selectors (#23) --- components/home/charts/open-interest.tsx | 112 +++++++++-------------- components/home/charts/retail-volume.tsx | 110 ++++++++++++---------- components/home/charts/volume-total.tsx | 103 +++++++++++---------- helpers/utils.ts | 62 ++++++++----- 4 files changed, 202 insertions(+), 185 deletions(-) diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index 3265f0c..fb1246c 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -9,26 +9,27 @@ import { Line, } from 'recharts'; import { useEffect, useState } from 'react'; -import { Box, Text, useMediaQuery } from '@chakra-ui/react'; +import { Box, Text } from '@chakra-ui/react'; import { useRequest } from '@/hooks/useRequest'; -import ChartWrapper, { CoinSelector } from '../../common/chartWrapper'; -import { BRIGHT_GREEN, CHART_HEIGHT, GREEN, YAXIS_WIDTH } from '../../../constants'; +import ChartWrapper from '../../common/chartWrapper'; +import { BRIGHT_GREEN, CHART_HEIGHT, YAXIS_WIDTH } from '../../../constants'; import { xAxisFormatter, - tooltipLabelFormatter, yaxisFormatter, tooltipFormatterCurrency, tooltipFormatterDate, } from '../../../helpers'; -import { getTokenColor } from '../../../constants/tokens'; +import { getTokenColor, initialTokensSelectedWithOther } from '../../../constants/tokens'; import { open_interest } from '../../../constants/api'; +import { createCoinSelectors } from '@/helpers/utils'; const REQUESTS = [open_interest]; -export default function VolumeChart(props: any) { - const isMobile = props.isMobile; - const [coinKeys, setCoinKeys] = useState([]); +export default function OpenInterestChart() { + const [coins, setCoins] = useState([]); + const initialTokensSelected = [...initialTokensSelectedWithOther, 'All']; + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const [formattedData, setFormattedData] = useState([]); const [dataOpenInterest, loadingOpenInterest, errorOpenInterest] = useRequest( @@ -44,79 +45,54 @@ export default function VolumeChart(props: any) { type GroupedOpenInterestData = { time: Date; unit: string; - all: number; + All: number; [key: string]: number | Date | string; }; - const groupByTime = (data: OpenInterestData[]): GroupedOpenInterestData[] => { + const groupByTime = (data: OpenInterestData[]): [GroupedOpenInterestData[], string[]] => { const map = new Map(); const totalOpenInterestMap = new Map(); + const uniqueCoins = new Set(); data.forEach((item) => { const key = item.time; if (!map.has(key)) { map.set(key, { time: new Date(key), - Other: 0, // Initialize the 'Other' property - all: 0, // Initialize the 'all' property - unit: '$', // Initialize the 'unit' property + All: 0, + unit: '$', }); } const existingEntry = map.get(key); existingEntry[item.coin] = (existingEntry[item.coin] || 0) + item.open_interest; - existingEntry.all += item.open_interest; // Aggregate total open interest for 'all' property + existingEntry.All += item.open_interest; - // Aggregate total open interest for each coin totalOpenInterestMap.set( item.coin, (totalOpenInterestMap.get(item.coin) || 0) + item.open_interest ); }); - // Get top 10 coins by total open interest - const top10Coins = Array.from(totalOpenInterestMap.entries()) - .sort(([, a], [, b]) => b - a) - .slice(0, 10) - .map(([coin]) => coin); - - // Filter out the coins that are not in the top 10 and calculate 'Other' - return Array.from(map.values()).map((record: any) => { - let otherSum = 0; - Object.keys(record).forEach((coin) => { - if (coin !== 'time' && coin !== 'unit' && coin !== 'all' && !top10Coins.includes(coin)) { - // Exclude 'unit' and 'all' from calculations - otherSum += record[coin]; - delete record[coin]; - } - }); - record.Other = otherSum; // Update 'Other' property with sum of other coins - return record as GroupedOpenInterestData; + map.forEach((entry) => { + const coinEntries = Object.entries(entry).filter( + ([key]) => + key !== 'time' && + key !== 'total' && + key !== 'cumulative' && + key !== 'other' && + key !== 'unit' && + key !== 'Other' + ); + coinEntries.forEach(([coin]) => uniqueCoins.add(coin)); }); - }; - - const extractUniqueCoins = (formattedVolumeData: GroupedOpenInterestData[]): string[] => { - const coinSet = new Set(); - for (const data of formattedVolumeData) { - Object.keys(data).forEach((coin) => { - if (coin !== 'all' && coin !== 'time' && coin !== 'unit') { - coinSet.add(coin); - } - }); - } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } - return coinsArray; + const result = Array.from(map.values()); + return [result, Array.from(uniqueCoins)]; }; const formatData = () => { - const groupedData = groupByTime(dataOpenInterest); - const uniqueCoins = extractUniqueCoins(groupedData); + const [groupedData, coins] = groupByTime(dataOpenInterest); + setCoins(coins); setFormattedData(groupedData); - setCoinKeys(uniqueCoins.sort()); }; useEffect(() => { @@ -125,8 +101,17 @@ export default function VolumeChart(props: any) { } }, [loading]); + const coinSelectors = createCoinSelectors( + coins, + coinsSelected, + setCoinsSelected, + formatData, + true, + 'All' + ); + return ( - + @@ -139,7 +124,7 @@ export default function VolumeChart(props: any) { /> - {coinKeys.map((coinName, i) => { + {coinsSelected.map((coinName, i) => { return ( ); })} - diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 2ec7d8f..d72faed 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -45,13 +45,12 @@ const REQUESTS = [ daily_usd_volume_by_user, ]; -export default function RetailVolumeChart(props: any) { - const isMobile = props.isMobile; - +export default function RetailVolumeChart() { const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); + const initialTokensSelected = [...initialTokensSelectedWithOther, 'Cumulative']; + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const [coinKeys, setCoinKeys] = useState([]); const [dataCumulativeUsdVolume, loadingCumulativeUsdVolume, errorCumulativeUsdVolume] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -144,7 +143,7 @@ export default function RetailVolumeChart(props: any) { return { time: new Date(time), ...selectedVolumes, - cumulative: formattedCumulativeUsdVolume[time as any], + Cumulative: formattedCumulativeUsdVolume[time as any], all: formattedDailyVolumeByTime[time as any], unit: '$', }; @@ -158,13 +157,7 @@ export default function RetailVolumeChart(props: any) { for (const data of formattedVolumeData) { coinSet.add(data.coin); } - const coinsArray = Array.from(coinSet); - if (coinsArray.includes('Other')) { - const index = coinsArray.indexOf('Other'); - coinsArray.splice(index, 1); - coinsArray.push('Other'); - } - + const coinsArray = ['Other', 'Cumulative', ...Array.from(coinSet)]; return coinsArray; }; @@ -192,11 +185,17 @@ export default function RetailVolumeChart(props: any) { } // Convert the collected data into an array const result: any[] = Object.entries(temp).map((item: any) => { + console.log( + '1111', + formattedCumulativeUsdVolume, + formattedCumulativeUsdVolume[item[0]], + item[0] + ); return { time: new Date(item[0]), maker: item[1].maker || 0, taker: item[1].taker || 0, - cumulative: formattedCumulativeUsdVolume[item[0]], + Cumulative: formattedCumulativeUsdVolume[item[0]], all: formattedDailyVolumeByTime[item[0]], unit: '$', }; @@ -244,8 +243,15 @@ export default function RetailVolumeChart(props: any) { } }, [loading, error]); - const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); - + const coinSelectors = createCoinSelectors( + coinKeys, + coinsSelected, + setCoinsSelected, + formatData, + false, + 'Cumulative' + ); + console.log('***', formattedDataCoins); return ( - {coinsSelected.map((coinName, i) => { - return ( - - ); + if (coinName !== 'Cumulative') { + return ( + + ); + } })} )} @@ -343,22 +342,35 @@ export default function RetailVolumeChart(props: any) { /> )} - + {coinsSelected.includes('Cumulative') && ( + <> + + + + )} - This measures two-sided volume, i.e. each side of a trade is counted once if that side is retail. + This measures two-sided volume, i.e. each side of a trade is counted once if that side is + retail. diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index 7425d82..7bf99a3 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -29,7 +29,8 @@ const REQUESTS = [total_volume]; export default function TotalVolumeChart() { const [formattedData, setFormattedData] = useState([]); - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); + const initialTokensSelected = [...initialTokensSelectedWithOther, 'Cumulative']; + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const [coins, setCoins] = useState([]); const [dataTotalVolume, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -43,7 +44,7 @@ export default function TotalVolumeChart() { time: Date; total: number; [coin: string]: any; - cumulative: number; + Cumulative: number; unit: string; Other: number; } @@ -55,16 +56,16 @@ export default function TotalVolumeChart() { const map = new Map(); const uniqueCoins = new Set(); - let cumulative = 0; + let Cumulative = 0; dataTotalVolume.forEach((item: TotalVolume) => { let { time, coin, total_volume } = item; - cumulative += total_volume; + Cumulative += total_volume; if (!map.has(time)) { map.set(time, { time: new Date(time), total: total_volume, [`${coin}`]: total_volume, - cumulative: cumulative, + Cumulative, Other: 0, unit: '$', }); @@ -72,19 +73,13 @@ export default function TotalVolumeChart() { const existingEntry = map.get(time)!; existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + total_volume; existingEntry.total += total_volume; - existingEntry.cumulative = cumulative; + existingEntry.Cumulative = Cumulative; } }); map.forEach((entry) => { const coinEntries = Object.entries(entry).filter( - ([key]) => - key !== 'time' && - key !== 'total' && - key !== 'cumulative' && - key !== 'other' && - key !== 'unit' && - key !== 'Other' + ([key]) => key !== 'time' && key !== 'total' && key !== 'other' && key !== 'unit' ); const otherCoins = coinEntries.filter( ([coin]) => !CoinsSelected.includes(coin) && coin !== 'all' @@ -93,8 +88,10 @@ export default function TotalVolumeChart() { coinEntries.forEach(([coin]) => uniqueCoins.add(coin)); let otherTotal = 0; - otherCoins.forEach(([_, value]) => { - otherTotal += value; + otherCoins.forEach(([key, value]) => { + if (key !== 'Cumulative') { + otherTotal += value; + } }); entry.Other = otherTotal; }); @@ -115,7 +112,14 @@ export default function TotalVolumeChart() { } }, [loading, error]); - const coinSelectors = createCoinSelectors(coins, coinsSelected, setCoinsSelected, formatData); + const coinSelectors = createCoinSelectors( + coins, + coinsSelected, + setCoinsSelected, + formatData, + false, + 'Cumulative' + ); return ( @@ -130,48 +134,53 @@ export default function TotalVolumeChart() { tickMargin={10} /> - {coinsSelected.map((coin, i) => { - return ( - + ); + } + })} + {coinsSelected.includes('Cumulative') && ( + <> + + - ); - })} - + + )} tooltipLabelFormatter(label, args, 'total')} diff --git a/helpers/utils.ts b/helpers/utils.ts index 2dd6abf..b6a0424 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -1,6 +1,12 @@ import { CoinSelector } from '../components/common/chartWrapper'; -const coinSelectorsSort = (a: CoinSelector, b: CoinSelector) => { +const coinSelectorsSort = (a: CoinSelector, b: CoinSelector, specialKey?: string) => { + if (a.name === specialKey) { + return -1; + } + if (b.name === specialKey) { + return 1; + } if (a.isChecked !== b.isChecked) { return a.isChecked ? -1 : 1; } @@ -11,31 +17,45 @@ export const createCoinSelectors = ( coinKeys: string[], coinsSelected: string[], setCoinsSelected: (arg: string[]) => any, - formatData: ((arg: string[]) => any) | (() => any) + formatData: ((arg: string[]) => any) | (() => any), + noOtherOption?: boolean, + specialKey?: string ) => { - return coinKeys - .map((coinKey: string) => { - return { - name: coinKey, - event: () => { - let newCoinsSelected = coinsSelected; - if (coinsSelected.includes(coinKey)) { - newCoinsSelected = coinsSelected.filter((e) => { - return e !== coinKey; - }); - } else { - newCoinsSelected.push(coinKey); - } + const emptySelection = noOtherOption ? [] : ['Other']; + const deselectAll = { + name: 'Deselect All', + event: () => { + setCoinsSelected(emptySelection); + }, + isChecked: coinsSelected.length === 0, + }; + + const coinSelectors = coinKeys.map((coinKey: string) => { + return { + name: coinKey, + event: () => { + let newCoinsSelected = [...coinsSelected]; + if (coinsSelected.includes(coinKey)) { + newCoinsSelected = newCoinsSelected.filter((e) => e !== coinKey); + } else { + newCoinsSelected.push(coinKey); + } + + if (typeof formatData === 'function') { if (formatData.length > 0) { formatData(newCoinsSelected); } else { const noArgsFormatData = formatData as () => any; noArgsFormatData(); } - setCoinsSelected(newCoinsSelected); - }, - isChecked: coinsSelected.includes(coinKey), - }; - }) - .sort((a: CoinSelector, b: CoinSelector) => coinSelectorsSort(a, b)); + } + setCoinsSelected(newCoinsSelected); + }, + isChecked: coinsSelected.includes(coinKey), + }; + }); + + const sortedCoinSelectors = coinSelectors.sort((a, b) => coinSelectorsSort(a, b, specialKey)); + + return [deselectAll, ...sortedCoinSelectors]; }; From 1e2ed0337cd3de7cc22d3019c946a467a3da6dc2 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Wed, 6 Sep 2023 19:47:43 -0400 Subject: [PATCH 44/59] Always display cumulative line (#24) --- components/home/charts/retail-volume.tsx | 117 +++++++++++------------ components/home/charts/volume-total.tsx | 86 ++++++++--------- helpers/index.ts | 18 ++-- 3 files changed, 107 insertions(+), 114 deletions(-) diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index d72faed..2894c00 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -49,8 +49,7 @@ export default function RetailVolumeChart() { const [dataMode, setDataMode] = useState<'COINS' | 'MARGIN'>('COINS'); const [formattedDataCoins, setFormattedDataCoins] = useState([]); const [formattedDataMargin, setFormattedDataMargin] = useState([]); - const initialTokensSelected = [...initialTokensSelectedWithOther, 'Cumulative']; - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coinKeys, setCoinKeys] = useState([]); const [dataCumulativeUsdVolume, loadingCumulativeUsdVolume, errorCumulativeUsdVolume] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -111,17 +110,23 @@ export default function RetailVolumeChart() { const formatVolumeByCoins = ( CoinsSelected: string[], - dataDailyUsdVolumeByCoin: VolumeData[], - formattedCumulativeUsdVolume: { [key: string]: number }, - formattedDailyVolumeByTime: { [key: string]: number } + dataDailyUsdVolumeByCoin: VolumeData[] ): FormattedVolumeData[] => { const temp: { [key: string]: { all: number; [coin: string]: number } } = {}; + const cumulativeByTime: { [key: string]: number } = {}; + let runningSelectedCumulative = 0; + let runningTotalCumulative = 0; for (const data of dataDailyUsdVolumeByCoin) { if (!temp[data.time]) { - temp[data.time] = { all: 0 }; + temp[data.time] = { all: 0, cumulative: 0 }; + } + if (CoinsSelected.includes(data.coin) || CoinsSelected.includes('Other')) { + temp[data.time].all += data.daily_usd_volume; + runningSelectedCumulative += data.daily_usd_volume; } temp[data.time][data.coin] = data.daily_usd_volume; - temp[data.time].all += data.daily_usd_volume; + runningTotalCumulative += data.daily_usd_volume; + cumulativeByTime[data.time] = runningSelectedCumulative; } const selectedCoinData = (obj: { [coin: string]: number }) => { @@ -131,20 +136,24 @@ export default function RetailVolumeChart() { const otherEntries = Object.entries(obj).filter( ([coin]) => !CoinsSelected.includes(coin) && coin !== 'all' ); - const otherVolume = otherEntries.reduce((total, [, volume]) => total + volume, 0); + const otherVolume = otherEntries.reduce((total, [_, volume]) => total + volume, 0); return { ...Object.fromEntries(selectedEntries), Other: otherVolume, + all: obj.all, }; }; const result: any[] = Object.entries(temp).map(([time, volumes]) => { const selectedVolumes = selectedCoinData(volumes); + const all = CoinsSelected.includes('Other') + ? selectedVolumes.Other + selectedVolumes.all + : selectedVolumes.all; return { time: new Date(time), ...selectedVolumes, - Cumulative: formattedCumulativeUsdVolume[time as any], - all: formattedDailyVolumeByTime[time as any], + cumulative: cumulativeByTime[time], + all, unit: '$', }; }); @@ -157,7 +166,7 @@ export default function RetailVolumeChart() { for (const data of formattedVolumeData) { coinSet.add(data.coin); } - const coinsArray = ['Other', 'Cumulative', ...Array.from(coinSet)]; + const coinsArray = ['Other', ...Array.from(coinSet)]; return coinsArray; }; @@ -185,12 +194,6 @@ export default function RetailVolumeChart() { } // Convert the collected data into an array const result: any[] = Object.entries(temp).map((item: any) => { - console.log( - '1111', - formattedCumulativeUsdVolume, - formattedCumulativeUsdVolume[item[0]], - item[0] - ); return { time: new Date(item[0]), maker: item[1].maker || 0, @@ -206,12 +209,7 @@ export default function RetailVolumeChart() { const formatData = (CoinsSelected: string[]) => { const formattedCumulativeVolumeByTime = formatCumulativeVolumeByTime(dataCumulativeUsdVolume); const formattedDailyVolumeByTime = formatDailyVolumeByTime(dataDailyUsdVolume); - const formattedVolumeByCoins = formatVolumeByCoins( - CoinsSelected, - dataDailyUsdVolumeByCoin, - formattedCumulativeVolumeByTime, - formattedDailyVolumeByTime - ); + const formattedVolumeByCoins = formatVolumeByCoins(CoinsSelected, dataDailyUsdVolumeByCoin); const formattedVolumeByCrossed = formatVolumeByCrossed( dataDailyUsdVolumeByCrossed, formattedCumulativeVolumeByTime, @@ -249,9 +247,8 @@ export default function RetailVolumeChart() { setCoinsSelected, formatData, false, - 'Cumulative' + 'Other' ); - console.log('***', formattedDataCoins); return ( {coinsSelected.map((coinName, i) => { - if (coinName !== 'Cumulative') { - return ( - - ); - } + return ( + + ); })} )} @@ -342,29 +337,25 @@ export default function RetailVolumeChart() { /> )} - {coinsSelected.includes('Cumulative') && ( - <> - - - - )} + + diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index 7bf99a3..c205702 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -44,7 +44,7 @@ export default function TotalVolumeChart() { time: Date; total: number; [coin: string]: any; - Cumulative: number; + cumulative: number; unit: string; Other: number; } @@ -56,24 +56,28 @@ export default function TotalVolumeChart() { const map = new Map(); const uniqueCoins = new Set(); - let Cumulative = 0; + let cumulative = 0; dataTotalVolume.forEach((item: TotalVolume) => { let { time, coin, total_volume } = item; - Cumulative += total_volume; + if (CoinsSelected.includes(coin) || CoinsSelected.includes('Other')) { + cumulative += total_volume; + } if (!map.has(time)) { map.set(time, { time: new Date(time), total: total_volume, [`${coin}`]: total_volume, - Cumulative, + cumulative, Other: 0, unit: '$', }); } else { const existingEntry = map.get(time)!; existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + total_volume; - existingEntry.total += total_volume; - existingEntry.Cumulative = Cumulative; + if (CoinsSelected.includes(coin) || CoinsSelected.includes('Other')) { + existingEntry.total += total_volume; + } + existingEntry.cumulative = cumulative; } }); @@ -89,7 +93,7 @@ export default function TotalVolumeChart() { let otherTotal = 0; otherCoins.forEach(([key, value]) => { - if (key !== 'Cumulative') { + if (key !== 'cumulative') { otherTotal += value; } }); @@ -118,7 +122,7 @@ export default function TotalVolumeChart() { setCoinsSelected, formatData, false, - 'Cumulative' + 'Other' ); return ( @@ -142,45 +146,39 @@ export default function TotalVolumeChart() { /> {coinsSelected.map((coin, i) => { - if (coin !== 'Cumulative') { - return ( - - ); - } - })} - {coinsSelected.includes('Cumulative') && ( - <> - - - - )} + ); + })} + + tooltipLabelFormatter(label, args, 'total')} diff --git a/helpers/index.ts b/helpers/index.ts index 9abb108..6a857e5 100644 --- a/helpers/index.ts +++ b/helpers/index.ts @@ -60,7 +60,11 @@ interface FormatNumberOpts { compact?: boolean; } -export const formatNumberWithOptions = (value: number, opts: FormatNumberOpts = {}): string => { +export const formatNumberWithOptions = ( + value: number, + opts: FormatNumberOpts = {}, + fixedDecimals?: number +): string => { const currency = !!opts.currency; const compact = !!opts.compact; const sign = value < 0 ? '-' : ''; @@ -71,7 +75,7 @@ export const formatNumberWithOptions = (value: number, opts: FormatNumberOpts = } const display = compact - ? formatNumberToCompactForm(absoluteValue) + ? formatNumberToCompactForm(absoluteValue, fixedDecimals) : getNumberFormatBasedOnValue(absoluteValue).format(absoluteValue); if (currency) { @@ -80,16 +84,16 @@ export const formatNumberWithOptions = (value: number, opts: FormatNumberOpts = return display; }; -export const formatNumberToCompactForm = (value: number): string => { +export const formatNumberToCompactForm = (value: number, fixedDecimals?: number): string => { const abs = Math.abs(value); if (abs >= 1e9) { - return `${(value / 1e9).toFixed(abs < 1e10 ? 2 : 1)}B`; + return `${(value / 1e9).toFixed(fixedDecimals !== undefined ? 0 : abs < 1e10 ? 2 : 1)}B`; } if (abs >= 1e6) { - return `${(value / 1e6).toFixed(abs < 1e7 ? 2 : 1)}M`; + return `${(value / 1e6).toFixed(fixedDecimals !== undefined ? 0 : abs < 1e7 ? 2 : 1)}M`; } if (abs >= 1e3) { - return `${(value / 1e3).toFixed(abs < 1e4 ? 2 : 1)}K`; + return `${(value / 1e3).toFixed(fixedDecimals !== undefined ? 0 : abs < 1e4 ? 2 : 1)}K`; } return `${value.toFixed(1)}`; }; @@ -181,7 +185,7 @@ export const yaxisFormatterNumber = (value: number): string => { }; export const yaxisFormatter = (value: number): string => { - return formatNumberWithOptions(value, { currency: true, compact: true }); + return formatNumberWithOptions(value, { currency: true, compact: true }, 0); }; export const tooltipFormatterNumber = (value: number | string): string => { From 284845278096b53ce175e019ef518f19b8d34bdd Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:03:02 -0400 Subject: [PATCH 45/59] Minor fix (#25) --- components/home/charts/volume-total.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/home/charts/volume-total.tsx b/components/home/charts/volume-total.tsx index c205702..2427602 100644 --- a/components/home/charts/volume-total.tsx +++ b/components/home/charts/volume-total.tsx @@ -29,8 +29,7 @@ const REQUESTS = [total_volume]; export default function TotalVolumeChart() { const [formattedData, setFormattedData] = useState([]); - const initialTokensSelected = [...initialTokensSelectedWithOther, 'Cumulative']; - const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); + const [coinsSelected, setCoinsSelected] = useState(initialTokensSelectedWithOther); const [coins, setCoins] = useState([]); const [dataTotalVolume, loading, error] = useRequest(REQUESTS[0], [], 'chart_data'); From f2d6228f9421af236e7bbb9138336a71398b6aa6 Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Wed, 6 Sep 2023 20:23:38 -0400 Subject: [PATCH 46/59] don't double count other volume --- components/home/charts/retail-volume.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 2894c00..f415a64 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -146,9 +146,10 @@ export default function RetailVolumeChart() { const result: any[] = Object.entries(temp).map(([time, volumes]) => { const selectedVolumes = selectedCoinData(volumes); - const all = CoinsSelected.includes('Other') - ? selectedVolumes.Other + selectedVolumes.all - : selectedVolumes.all; + const all = + CoinsSelected.length === 1 && CoinsSelected.includes('Other') + ? selectedVolumes.Other + : selectedVolumes.all; return { time: new Date(time), ...selectedVolumes, From 73ebfd98441e88a4b3f6ab4613b9f2fc252b6c5d Mon Sep 17 00:00:00 2001 From: tradermohamed Date: Wed, 13 Sep 2023 16:13:31 -0400 Subject: [PATCH 47/59] add 30k & 100k to slippage chart --- components/home/charts/liquidity.tsx | 88 ++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index b0ed7f4..eb32597 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -25,19 +25,21 @@ import { liquidity_by_coin } from '../../../constants/api'; const REQUESTS = [liquidity_by_coin]; -export default function Liquidity(props: any) { - const isMobile = props.isMobile; - +export default function Liquidity() { const [formattedData0, setFormattedData0] = useState([]); const [formattedData1000, setFormattedData1000] = useState([]); const [formattedData10000, setFormattedData10000] = useState([]); + const [formattedData30000, setFormattedData30000] = useState([]); + const [formattedData100000, setFormattedData100000] = useState([]); const [coinKeys, setCoinKeys] = useState([]); const [coinKeys0, setCoinKeys0] = useState([]); const [coinKeys1000, setCoinKeys1000] = useState([]); const [coinKeys10000, setCoinKeys10000] = useState([]); + const [coinKeys30000, setCoinKeys30000] = useState([]); + const [coinKeys100000, setCoinKeys100000] = useState([]); - const [dataMode, setDataMode] = useState<'0' | '1000' | '10000'>('0'); + const [dataMode, setDataMode] = useState<'0' | '1000' | '10000'| '30000'| '100000'>('0'); const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -61,6 +63,16 @@ export default function Liquidity(props: any) { event: () => setDataMode('10000'), active: dataMode === '10000', }, + { + text: '$30k', + event: () => setDataMode('30000'), + active: dataMode === '30000', + }, + { + text: '$100k', + event: () => setDataMode('100000'), + active: dataMode === '100000', + }, ], }; @@ -69,6 +81,8 @@ export default function Liquidity(props: any) { median_slippage_0: number; median_slippage_1000: number; median_slippage_10000: number; + median_slippage_30000: number; + median_slippage_100000: number; time: string; }[]; }; @@ -77,6 +91,8 @@ export default function Liquidity(props: any) { median_slippage_0: { time: Date; [key: string]: number | Date | string }[]; median_slippage_1000: { time: Date; [key: string]: number | Date | string }[]; median_slippage_10000: { time: Date; [key: string]: number | Date | string }[]; + median_slippage_30000: { time: Date; [key: string]: number | Date | string }[]; + median_slippage_100000: { time: Date; [key: string]: number | Date | string }[]; }; const extractCoins = (data: InputData): string[] => { @@ -106,6 +122,14 @@ export default function Liquidity(props: any) { string, { time: Date; [key: string]: number | Date | string } >(); + const median_slippage_30000 = new Map< + string, + { time: Date; [key: string]: number | Date | string } + >(); + const median_slippage_100000 = new Map< + string, + { time: Date; [key: string]: number | Date | string } +>(); for (let key in filteredData) { if (!filteredData[key]) { @@ -117,19 +141,29 @@ export default function Liquidity(props: any) { median_slippage_0: val_0, median_slippage_1000: val_1000, median_slippage_10000: val_10000, + median_slippage_30000: val_30000, + median_slippage_100000: val_100000, + } = record; const map0 = median_slippage_0.get(time) || { time: new Date(time), unit: '%' }; const map1000 = median_slippage_1000.get(time) || { time: new Date(time), unit: '%' }; const map10000 = median_slippage_10000.get(time) || { time: new Date(time), unit: '%' }; + const map30000 = median_slippage_30000.get(time) || { time: new Date(time), unit: '%' }; + const map100000 = median_slippage_100000.get(time) || { time: new Date(time), unit: '%' }; map0[key] = val_0 * 100; map1000[key] = val_1000 * 100; map10000[key] = val_10000 * 100; + map30000[key] = val_30000 * 100; + map100000[key] = val_100000 * 100; median_slippage_0.set(time, map0); median_slippage_1000.set(time, map1000); median_slippage_10000.set(time, map10000); + median_slippage_30000.set(time, map30000); + median_slippage_100000.set(time, map100000); + }); } @@ -147,6 +181,12 @@ export default function Liquidity(props: any) { median_slippage_10000: Array.from(median_slippage_10000.values()).sort((a, b) => sortByDate(a.time, b.time) ), + median_slippage_30000: Array.from(median_slippage_30000.values()).sort((a, b) => + sortByDate(a.time, b.time) + ), + median_slippage_100000: Array.from(median_slippage_100000.values()).sort((a, b) => + sortByDate(a.time, b.time) + ), }; }; @@ -171,13 +211,20 @@ export default function Liquidity(props: any) { setFormattedData0(formattedData.median_slippage_0); setFormattedData1000(formattedData.median_slippage_1000); setFormattedData10000(formattedData.median_slippage_10000); + setFormattedData30000(formattedData.median_slippage_30000); + setFormattedData100000(formattedData.median_slippage_100000); + const formattedUniqueCoinKeys0 = extractUniqueCoins(formattedData.median_slippage_0); const formattedUniqueCoinKeys1000 = extractUniqueCoins(formattedData.median_slippage_1000); const formattedUniqueCoinKeys10000 = extractUniqueCoins(formattedData.median_slippage_10000); + const formattedUniqueCoinKeys30000 = extractUniqueCoins(formattedData.median_slippage_30000); + const formattedUniqueCoinKeys100000 = extractUniqueCoins(formattedData.median_slippage_100000); setCoinKeys0(formattedUniqueCoinKeys0); setCoinKeys1000(formattedUniqueCoinKeys1000); setCoinKeys10000(formattedUniqueCoinKeys10000); + setCoinKeys30000(formattedUniqueCoinKeys30000); + setCoinKeys100000(formattedUniqueCoinKeys100000); }; useEffect(() => { @@ -186,15 +233,30 @@ export default function Liquidity(props: any) { } }, [loading]); - const chartData = - dataMode === '0' - ? formattedData0 - : dataMode === '1000' - ? formattedData1000 - : formattedData10000; - - const chartDataCoinKeys = - dataMode === '0' ? coinKeys0 : dataMode === '1000' ? coinKeys1000 : coinKeys10000; + let chartData; + let chartDataCoinKeys; + switch (dataMode) { + case '0': + chartData = formattedData0; + chartDataCoinKeys = coinKeys0; + break; + case '1000': + chartData = formattedData1000; + chartDataCoinKeys = coinKeys1000; + break; + case '10000': + chartData = formattedData10000; + chartDataCoinKeys = coinKeys10000; + break; + case '30000': + chartData = formattedData30000; + chartDataCoinKeys = coinKeys30000; + break; + case '100000': + chartData = formattedData100000; + chartDataCoinKeys = coinKeys100000; + break; + } const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); From 44244439a732da539a8a6379630af55279ab16d7 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Fri, 15 Sep 2023 11:21:12 -0400 Subject: [PATCH 48/59] Slippage graph fixes (#26) --- components/home/charts/liquidity.tsx | 50 +++++++++++++++++----------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index eb32597..8a41f9e 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -22,6 +22,7 @@ import { createCoinSelectors } from '../../../helpers/utils'; import { getTokenColor, initialTokensSelected } from '../../../constants/tokens'; import { liquidity_by_coin } from '../../../constants/api'; +import { Box, Text } from '@chakra-ui/react'; const REQUESTS = [liquidity_by_coin]; @@ -39,7 +40,7 @@ export default function Liquidity() { const [coinKeys30000, setCoinKeys30000] = useState([]); const [coinKeys100000, setCoinKeys100000] = useState([]); - const [dataMode, setDataMode] = useState<'0' | '1000' | '10000'| '30000'| '100000'>('0'); + const [dataMode, setDataMode] = useState<'0' | '1000' | '10000' | '30000' | '100000'>('0'); const [coinsSelected, setCoinsSelected] = useState(initialTokensSelected); const [dataLiqudity, loadingLiqudity, errorLiqudity] = useRequest(REQUESTS[0], [], 'chart_data'); @@ -107,7 +108,9 @@ export default function Liquidity() { // Filter data for each category by top 10 coins const filteredData: InputData = {}; for (let coin of coinsSelected) { - filteredData[coin] = data[coin]; + if (coinsSelected.includes(coin)) { + filteredData[coin] = data[coin]; + } } const median_slippage_0 = new Map< @@ -123,13 +126,13 @@ export default function Liquidity() { { time: Date; [key: string]: number | Date | string } >(); const median_slippage_30000 = new Map< - string, - { time: Date; [key: string]: number | Date | string } - >(); - const median_slippage_100000 = new Map< - string, - { time: Date; [key: string]: number | Date | string } ->(); + string, + { time: Date; [key: string]: number | Date | string } + >(); + const median_slippage_100000 = new Map< + string, + { time: Date; [key: string]: number | Date | string } + >(); for (let key in filteredData) { if (!filteredData[key]) { @@ -143,7 +146,6 @@ export default function Liquidity() { median_slippage_10000: val_10000, median_slippage_30000: val_30000, median_slippage_100000: val_100000, - } = record; const map0 = median_slippage_0.get(time) || { time: new Date(time), unit: '%' }; @@ -163,7 +165,6 @@ export default function Liquidity() { median_slippage_10000.set(time, map10000); median_slippage_30000.set(time, map30000); median_slippage_100000.set(time, map100000); - }); } @@ -182,11 +183,11 @@ export default function Liquidity() { sortByDate(a.time, b.time) ), median_slippage_30000: Array.from(median_slippage_30000.values()).sort((a, b) => - sortByDate(a.time, b.time) - ), - median_slippage_100000: Array.from(median_slippage_100000.values()).sort((a, b) => - sortByDate(a.time, b.time) - ), + sortByDate(a.time, b.time) + ), + median_slippage_100000: Array.from(median_slippage_100000.values()).sort((a, b) => + sortByDate(a.time, b.time) + ), }; }; @@ -231,7 +232,7 @@ export default function Liquidity() { if (!loading && !error) { formatData(); } - }, [loading]); + }, [loading, coinKeys]); let chartData; let chartDataCoinKeys; @@ -258,11 +259,17 @@ export default function Liquidity() { break; } - const coinSelectors = createCoinSelectors(coinKeys, coinsSelected, setCoinsSelected, formatData); + const coinSelectors = createCoinSelectors( + coinKeys, + coinsSelected, + setCoinsSelected, + formatData, + true + ); return ( - {chartDataCoinKeys.map((coinName, i) => { + {coinsSelected.map((coinName, i) => { return ( + + Slippage percentage by tade size. + ); } From 2c6e07caa4bd04f0b9b490b61e03f7104fae9aa4 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Wed, 25 Oct 2023 13:40:03 -0400 Subject: [PATCH 49/59] HLP hedged pnl fix (#27) --- components/home/charts/hlp.tsx | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index 24db7eb..ec1edf3 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -61,6 +61,7 @@ export default function Hlp() { coin: string; avg_oracle_px: number; first_oracle_px: number; + last_oracle_px: number; avg_open_interest: number; }; @@ -77,12 +78,15 @@ export default function Hlp() { hedged_pnl: number; }; - const getOraclePxs = (assetCtxs: AssetCtx[]): Map => { - const map = new Map(); + const getOraclePxs = (assetCtxs: AssetCtx[]): {firstOraclePxs: Map, lastOraclePxs: Map} => { + const firstOraclePxs = new Map(); + const lastOraclePxs = new Map(); + assetCtxs.forEach((item) => { - map.set(item.coin + item.time, item.first_oracle_px); + firstOraclePxs.set(item.coin + item.time, item.first_oracle_px); + lastOraclePxs.set(item.coin + item.time, item.last_oracle_px) }); - return map; + return {firstOraclePxs, lastOraclePxs}; }; const makeHlpPnl = ( @@ -122,9 +126,8 @@ export default function Hlp() { const map = new Map(); const uniqueTopCoins = new Set(); - let prevTime: string | null = null; let hedgedCumulativePnl = 0; - const oraclePxs = getOraclePxs(assetCtxs); + const {firstOraclePxs, lastOraclePxs} = getOraclePxs(assetCtxs); hlpPositions.forEach((item: HlpPosition) => { let { time, coin, daily_ntl } = item; @@ -138,23 +141,20 @@ export default function Hlp() { hedged_cumulative_pnl: hedgedCumulativePnl, Other: 0, }); - prevTime = time; } const existingEntry = map.get(time)!; existingEntry[`${coin}`] = (existingEntry[`${coin}`] || 0) + daily_ntl; existingEntry.daily_ntl += daily_ntl; - const oraclePx = oraclePxs.get(coin + time); + const firstOraclePx = firstOraclePxs.get(coin + time); let hedgedPnl = 0; - const nextTime = getNextTime(time); - let oraclePxNext = oraclePxs.get(coin + nextTime); - let prevTimeData = prevTime ? map.get(prevTime) : null; - let prevDayNtlPosition = prevTimeData ? prevTimeData[`${coin}`] : null; - - if (oraclePxNext && oraclePx && prevDayNtlPosition) { - const pxChange = 1 - oraclePx / oraclePxNext; - const pnl = -1 * prevDayNtlPosition * pxChange; + let lastOraclePx = lastOraclePxs.get(coin + time); + let curDayData = map.get(time); + let coinNtlPosition = curDayData ? curDayData[`${coin}`] : null; + if (lastOraclePx && firstOraclePx && coinNtlPosition) { + const pxChange = 1 - firstOraclePx / lastOraclePx; + const pnl = -1 * coinNtlPosition * pxChange; hedgedPnl += pnl; } From c392e2bb6984ff1482f792ecc8f95f88380c16e9 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Fri, 8 Dec 2023 14:37:07 -0500 Subject: [PATCH 50/59] Fix empty selection (#28) --- helpers/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/helpers/utils.ts b/helpers/utils.ts index b6a0424..63976cb 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -26,6 +26,7 @@ export const createCoinSelectors = ( name: 'Deselect All', event: () => { setCoinsSelected(emptySelection); + formatData(emptySelection); }, isChecked: coinsSelected.length === 0, }; From c97c827d6b3e982ed14e5deadeb07f7795b8ebf9 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:27:51 -0500 Subject: [PATCH 51/59] Fix Decimals (#29) --- components/home/charts/retail-volume.tsx | 6 +++--- components/home/charts/unique-users-coin.tsx | 10 +++++----- helpers/index.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index f415a64..167a463 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -252,7 +252,7 @@ export default function RetailVolumeChart() { ); return ( - This measures two-sided volume, i.e. each side of a trade is counted once if that side is - retail. + This measures two-sided volume, i.e. each side of a trade is counted once if that side is retail. + This previously tracked retail volume, but was changed in February, 2024 to non-HLP volume. diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 6167647..3bdfed2 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -182,7 +182,7 @@ export default function UniqueUsers() { return ( - The line is the number of unique addresses who used Hyperliquid each day, bars represent - proportion of users who traded specific coins. Total exceeds 100% as users can trade - multiple coins. Top 10 coins are shown separately and the rest are grouped as Other. + The line is the number of unique addresses who traded on Hyperliquid each day, bars represent + proportion of users who traded specific coins. Total exceeds 100% as users can trade + multiple coins. Top 10 coins are shown separately and the rest are grouped as Other. diff --git a/helpers/index.ts b/helpers/index.ts index 6a857e5..dd41f7c 100644 --- a/helpers/index.ts +++ b/helpers/index.ts @@ -185,7 +185,7 @@ export const yaxisFormatterNumber = (value: number): string => { }; export const yaxisFormatter = (value: number): string => { - return formatNumberWithOptions(value, { currency: true, compact: true }, 0); + return formatNumberWithOptions(value, { currency: true, compact: true }); }; export const tooltipFormatterNumber = (value: number | string): string => { From 75ba69b050cb36edccfc29856898a8548024071f Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:31:24 -0500 Subject: [PATCH 52/59] Language Fixes (#30) --- components/home/charts/hlp.tsx | 4 ---- components/home/charts/liquidity.tsx | 2 +- components/home/charts/open-interest.tsx | 5 ----- components/home/charts/retail-volume.tsx | 2 +- components/home/charts/unique-users-coin.tsx | 2 +- 5 files changed, 3 insertions(+), 12 deletions(-) diff --git a/components/home/charts/hlp.tsx b/components/home/charts/hlp.tsx index ec1edf3..e0b338c 100644 --- a/components/home/charts/hlp.tsx +++ b/components/home/charts/hlp.tsx @@ -345,10 +345,6 @@ export default function Hlp() { - - {dataMode === 'PNL' && PNL over time} - - {dataMode === 'HEDGED' && ( diff --git a/components/home/charts/liquidity.tsx b/components/home/charts/liquidity.tsx index 8a41f9e..60a7abd 100644 --- a/components/home/charts/liquidity.tsx +++ b/components/home/charts/liquidity.tsx @@ -319,7 +319,7 @@ export default function Liquidity() { - Slippage percentage by tade size. + Slippage percentage by trade size. ); diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index fb1246c..b6170e8 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -161,11 +161,6 @@ export default function OpenInterestChart() { })} - - - Top 10 Coins grouped by total volume over time and remaining coins grouped by Other - - ); } diff --git a/components/home/charts/retail-volume.tsx b/components/home/charts/retail-volume.tsx index 167a463..8f5f8ed 100644 --- a/components/home/charts/retail-volume.tsx +++ b/components/home/charts/retail-volume.tsx @@ -362,7 +362,7 @@ export default function RetailVolumeChart() { This measures two-sided volume, i.e. each side of a trade is counted once if that side is retail. - This previously tracked retail volume, but was changed in February, 2024 to non-HLP volume. + This previously tracked retail volume, but was changed in February 2024 to non-HLP volume. diff --git a/components/home/charts/unique-users-coin.tsx b/components/home/charts/unique-users-coin.tsx index 3bdfed2..b32bffa 100644 --- a/components/home/charts/unique-users-coin.tsx +++ b/components/home/charts/unique-users-coin.tsx @@ -260,7 +260,7 @@ export default function UniqueUsers() { The line is the number of unique addresses who traded on Hyperliquid each day, bars represent proportion of users who traded specific coins. Total exceeds 100% as users can trade - multiple coins. Top 10 coins are shown separately and the rest are grouped as Other. + multiple coins. From fa802b83563437bc192f6dd8e932fcc797516bb3 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:02:03 -0500 Subject: [PATCH 53/59] update tokens (#31) --- constants/tokens.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/constants/tokens.ts b/constants/tokens.ts index 2d9321a..17e485a 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -1,16 +1,6 @@ import * as CryptoJS from 'crypto-js'; -export const initialTokensSelected = [ - 'ARB', - 'ATOM', - 'AVAX', - 'BNB', - 'BTC', - 'COMP', - 'CRV', - 'DOGE', - 'ETH', -]; +export const initialTokensSelected = ['BTC', 'ETH', 'SOL']; export const initialTokensSelectedWithOther = [...initialTokensSelected, 'Other']; export function getTokenColor(token: string): string { From 1831ff960e1579be38e1e655b6da3b954ae3aa68 Mon Sep 17 00:00:00 2001 From: tradermohamed <110409704+tradermohamed@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:10:07 -0500 Subject: [PATCH 54/59] set sol colour to orange (#32) --- constants/tokens.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/constants/tokens.ts b/constants/tokens.ts index 17e485a..d54f685 100644 --- a/constants/tokens.ts +++ b/constants/tokens.ts @@ -7,6 +7,9 @@ export function getTokenColor(token: string): string { if (token == 'Other') { return 'pink'; } + if (token == 'SOL') { + return 'orange'; + } // Use the CryptoJS library to get the MD5 hash of the string let hash = CryptoJS.MD5('col' + token); From 0f2e3e30a2c593506ea32be6a37a63a864d70383 Mon Sep 17 00:00:00 2001 From: lmlmt Date: Tue, 7 May 2024 07:42:48 +0000 Subject: [PATCH 55/59] update --- components/home/charts/date-range.tsx | 3 +- components/home/charts/open-interest.tsx | 5 + contexts/data.tsx | 3 +- package-lock.json | 952 +++++++++++++++++++++++ package.json | 5 +- 5 files changed, 965 insertions(+), 3 deletions(-) diff --git a/components/home/charts/date-range.tsx b/components/home/charts/date-range.tsx index 75a24fb..9c45ed6 100644 --- a/components/home/charts/date-range.tsx +++ b/components/home/charts/date-range.tsx @@ -9,7 +9,8 @@ import 'react-date-range/dist/styles.css'; import 'react-date-range/dist/theme/default.css'; const ALL_TIME_ID = 4; -const DATA_START_DATE = new Date('2023-06-14T20:00:00.000'); +// const DATA_START_DATE = new Date('2023-06-14T20:00:00.000'); +const DATA_START_DATE = new Date('2024-03-11T20:00:00.000'); const DATE_NOW = new Date(); export const DateRangeSelect = () => { diff --git a/components/home/charts/open-interest.tsx b/components/home/charts/open-interest.tsx index b6170e8..9c12323 100644 --- a/components/home/charts/open-interest.tsx +++ b/components/home/charts/open-interest.tsx @@ -161,6 +161,11 @@ export default function OpenInterestChart() { })} + + + Before March 22, 2024, this showed one-sided open interest. It now shows two-sided open interest to address user confusion. + + ); } diff --git a/contexts/data.tsx b/contexts/data.tsx index 91b74a5..89e7636 100644 --- a/contexts/data.tsx +++ b/contexts/data.tsx @@ -29,7 +29,8 @@ export const DataContextProvider = (props: any) => { const initState: State = { dates: { - from: '2023-06-14', + // from: '2023-06-14', + from: '2024-03-11', to: DATE_TO, }, setDates: setDates, diff --git a/package-lock.json b/package-lock.json index fc5cc19..a27081d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,8 @@ "eslint": "8.41.0", "eslint-config-next": "13.4.4", "ethers": "^5.7.2", + "express": "^4.19.2", + "express-rate-limit": "^7.2.0", "framer-motion": "^10.12.16", "lodash": "^4.17.21", "mixpanel-browser": "^2.47.0", @@ -31,6 +33,7 @@ "next": "^13.4.4", "polished": "^4.2.2", "prettier": "^3.0.0", + "rate-limiter-flexible": "^5.0.3", "react": "18.2.0", "react-date-range": "^1.4.0", "react-dom": "18.2.0", @@ -4701,6 +4704,18 @@ "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.2.2.tgz", "integrity": "sha512-0j2gZq8HiZ51z4zNnSkF1iSkqlwRDvdH+son3wHdoz+7IUdMN/5Exd4TxMJ+gq2Of1DiXReYLL9qqh2PdQ4wgA==" }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -4795,6 +4810,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, "node_modules/array-includes": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", @@ -5002,6 +5022,42 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -5099,6 +5155,14 @@ "node": ">=10.16.0" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -5206,11 +5270,43 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", @@ -5610,6 +5706,23 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -5705,6 +5818,11 @@ "tslib": "^2.0.3" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, "node_modules/electron-to-chromium": { "version": "1.4.414", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.414.tgz", @@ -5734,6 +5852,14 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.14.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", @@ -5876,6 +6002,11 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -6363,6 +6494,14 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/ethers": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", @@ -6437,6 +6576,74 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-rate-limit": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.2.0.tgz", + "integrity": "sha512-T7nul1t4TNyfZMJ7pKRKkdeVJWa2CqB8NA1P8BwYaoDI5QSBZARv5oMS43J7b7I5P+4asjVXjb7ONuwDKucahg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "4 || 5 || ^5.0.0-beta.1" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6516,6 +6723,36 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -6604,6 +6841,14 @@ "node": ">= 6" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/framer-motion": { "version": "10.12.16", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.12.16.tgz", @@ -6655,6 +6900,14 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6950,6 +7203,21 @@ "react-is": "^16.7.0" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/human-signals": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", @@ -6958,6 +7226,17 @@ "node": ">=14.18.0" } }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -7032,6 +7311,14 @@ "loose-envify": "^1.0.0" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -7549,6 +7836,19 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7562,6 +7862,14 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -7574,6 +7882,17 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -7673,6 +7992,14 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next": { "version": "13.4.4", "resolved": "https://registry.npmjs.org/next/-/next-13.4.4.tgz", @@ -7885,6 +8212,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -7996,6 +8334,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8025,6 +8371,11 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -8120,6 +8471,18 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -8133,6 +8496,20 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8152,6 +8529,33 @@ } ] }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rate-limiter-flexible": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-5.0.3.tgz", + "integrity": "sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==" + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -8747,6 +9151,25 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -8760,6 +9183,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -8781,6 +9209,66 @@ "semver": "bin/semver.js" } }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, "node_modules/shallow-equal": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", @@ -8856,6 +9344,14 @@ "node": ">=0.10.0" } }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -9129,6 +9625,14 @@ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -9197,6 +9701,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -9272,6 +9788,14 @@ "node": ">=4" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", @@ -9358,6 +9882,22 @@ } } }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/victory-vendor": { "version": "36.6.10", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.10.tgz", @@ -12655,6 +13195,15 @@ "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.2.2.tgz", "integrity": "sha512-0j2gZq8HiZ51z4zNnSkF1iSkqlwRDvdH+son3wHdoz+7IUdMN/5Exd4TxMJ+gq2Of1DiXReYLL9qqh2PdQ4wgA==" }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, "acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -12725,6 +13274,11 @@ "is-array-buffer": "^3.0.1" } }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, "array-includes": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", @@ -12885,6 +13439,40 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, + "body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -12947,6 +13535,11 @@ "streamsearch": "^1.1.0" } }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -13022,11 +13615,34 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, "convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, + "cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, "copy-to-clipboard": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", @@ -13314,6 +13930,16 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, "detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -13385,6 +14011,11 @@ "tslib": "^2.0.3" } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, "electron-to-chromium": { "version": "1.4.414", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.414.tgz", @@ -13416,6 +14047,11 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, "enhanced-resolve": { "version": "5.14.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", @@ -13528,6 +14164,11 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -13881,6 +14522,11 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, "ethers": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", @@ -13939,6 +14585,65 @@ "strip-final-newline": "^3.0.0" } }, + "express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "express-rate-limit": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.2.0.tgz", + "integrity": "sha512-T7nul1t4TNyfZMJ7pKRKkdeVJWa2CqB8NA1P8BwYaoDI5QSBZARv5oMS43J7b7I5P+4asjVXjb7ONuwDKucahg==", + "requires": {} + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -14005,6 +14710,35 @@ "to-regex-range": "^5.0.1" } }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -14064,6 +14798,11 @@ "mime-types": "^2.1.12" } }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, "framer-motion": { "version": "10.12.16", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.12.16.tgz", @@ -14105,6 +14844,11 @@ } } }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -14310,11 +15054,31 @@ "react-is": "^16.7.0" } }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, "human-signals": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==" }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -14371,6 +15135,11 @@ "loose-envify": "^1.0.0" } }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -14725,6 +15494,16 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -14735,6 +15514,11 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -14744,6 +15528,11 @@ "picomatch": "^2.3.1" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -14810,6 +15599,11 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, "next": { "version": "13.4.4", "resolved": "https://registry.npmjs.org/next/-/next-13.4.4.tgz", @@ -14944,6 +15738,14 @@ "es-abstract": "^1.20.4" } }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -15019,6 +15821,11 @@ "lines-and-columns": "^1.1.6" } }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -15039,6 +15846,11 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -15097,6 +15909,15 @@ "react-is": "^16.13.1" } }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -15107,11 +15928,40 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "rate-limiter-flexible": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-5.0.3.tgz", + "integrity": "sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==" + }, + "raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -15503,6 +16353,11 @@ "queue-microtask": "^1.2.2" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, "safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -15513,6 +16368,11 @@ "is-regex": "^1.1.4" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -15531,6 +16391,64 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, "shallow-equal": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", @@ -15588,6 +16506,11 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, "stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -15765,6 +16688,11 @@ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, "tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -15819,6 +16747,15 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, "typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -15869,6 +16806,11 @@ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, "untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", @@ -15908,6 +16850,16 @@ "tslib": "^2.0.0" } }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, "victory-vendor": { "version": "36.6.10", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.10.tgz", diff --git a/package.json b/package.json index 3febaf4..53737de 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "scripts": { "dev": "next dev", "build": "next build", - "start": "next start", + "start": "node server.js", "lint": "npx eslint . --fix", "prettify": "npx prettier --write ." }, @@ -73,6 +73,8 @@ "eslint": "8.41.0", "eslint-config-next": "13.4.4", "ethers": "^5.7.2", + "express": "^4.19.2", + "express-rate-limit": "^7.2.0", "framer-motion": "^10.12.16", "lodash": "^4.17.21", "mixpanel-browser": "^2.47.0", @@ -80,6 +82,7 @@ "next": "^13.4.4", "polished": "^4.2.2", "prettier": "^3.0.0", + "rate-limiter-flexible": "^5.0.3", "react": "18.2.0", "react-date-range": "^1.4.0", "react-dom": "18.2.0", From 17f953f26abad7bc23aede7102d55175e905393b Mon Sep 17 00:00:00 2001 From: lmlmt Date: Tue, 7 May 2024 08:33:03 +0000 Subject: [PATCH 56/59] revert server change --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 53737de..50c229e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "scripts": { "dev": "next dev", "build": "next build", - "start": "node server.js", + "start": "next start", "lint": "npx eslint . --fix", "prettify": "npx prettier --write ." }, From 462ddf7e359e9542a44d640f0ab89745351e926d Mon Sep 17 00:00:00 2001 From: Pascal Mugnier Date: Thu, 9 May 2024 03:30:07 +0200 Subject: [PATCH 57/59] Use data from S3 instead of API (#34) Co-authored-by: Pascal Mugnier --- .env | 2 +- constants/api.ts | 104 ++++++++++++++++++++++---------------------- hooks/useRequest.ts | 59 +++++++++++-------------- next.config.js | 1 + 4 files changed, 78 insertions(+), 88 deletions(-) diff --git a/.env b/.env index 6f5ec49..0ace994 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -NEXT_PUBLIC_API_URL = https://stats-api.hyperliquid.xyz +NEXT_PUBLIC_DAT_URL = https://d2v1fiwobg9w6.cloudfront.net NEXT_PUBLIC_GOOGLE_ANALYTICS_CONFIG = G-8GN39Z428L diff --git a/constants/api.ts b/constants/api.ts index 7d1fb8a..3fefe63 100644 --- a/constants/api.ts +++ b/constants/api.ts @@ -1,56 +1,54 @@ -export const total_users = '/hyperliquid/total_users'; // Retrieves the total number of users. -export const total_usd_volume = '/hyperliquid/total_usd_volume'; // Retrieves the total USD trading volume. -export const total_deposits = '/hyperliquid/total_deposits'; // Retrieves the total amount of deposits. -export const total_withdrawals = '/hyperliquid/total_withdrawals'; // Retrieves the total amount of withdrawals. -export const total_notional_liquidated = '/hyperliquid/total_notional_liquidated'; // Retrieves the total notional value liquidated. -export const cumulative_usd_volume = '/hyperliquid/cumulative_usd_volume'; // Retrieves the cumulative USD trading volume over time. -export const daily_usd_volume = '/hyperliquid/daily_usd_volume'; // Retrieves the daily USD trading volume over time. -export const daily_usd_volume_by_coin = '/hyperliquid/daily_usd_volume_by_coin'; // Retrieves the daily USD trading volume by coin over time. -export const daily_usd_volume_by_crossed = '/hyperliquid/daily_usd_volume_by_crossed'; // Retrieves the daily USD trading volume by crossed over time. -export const daily_usd_volume_by_user = '/hyperliquid/daily_usd_volume_by_user'; // Retrieves the daily USD trading volume by top 10 user and the rest are summed and marked as other. - -export const cumulative_trades = '/hyperliquid/cumulative_trades'; // Retrieves the cumulative number of trades over time. The line chart of Cumulative total trades chart. - -export const daily_trades = '/hyperliquid/daily_trades'; // Retrieves the daily number of trades. -export const daily_trades_by_coin = '/hyperliquid/daily_trades_by_coin'; // Retrieves the daily number of trades by coin. -export const daily_trades_by_crossed = '/hyperliquid/daily_trades_by_crossed'; // Retrieves the daily number of trades by crossed. -export const daily_trades_by_user = '/hyperliquid/daily_trades_by_user'; // Retrieves the daily number of trades by top 10 user and the rest are summed and marked as other. -export const user_pnl = '/hyperliquid/user_pnl'; // Retrieves the profit and loss (PnL) for all users daily. -export const cumulative_user_pnl = '/hyperliquid/cumulative_user_pnl'; // Retrieves the cumulative PnL for all users over time. -export const hlp_liquidator_pnl = '/hyperliquid/hlp_liquidator_pnl'; // Retrieves the PnL for liquidators daily. -export const hlp_liquidator_pnl_false = '/hyperliquid/hlp_liquidator_pnl?is_hlp=false'; // Retrieves the PnL for liquidators daily. - -export const cumulative_hlp_liquidator_pnl = '/hyperliquid/cumulative_hlp_liquidator_pnl'; // Retrieves the cumulative PnL for liquidators over time. - -export const cumulative_hlp_liquidator_pnl_false = - '/hyperliquid/cumulative_hlp_liquidator_pnl?is_hlp=false'; // Retrieves the cumulative PnL for liquidators over time. - -export const cumulative_liquidated_notional = '/hyperliquid/cumulative_liquidated_notional'; // Retrieves the cumulative liquidated notional value over time. -export const daily_unique_users = '/hyperliquid/daily_unique_users'; // Retrieves the daily number of unique users. -export const cumulative_users = '/hyperliquid/cumulative_users'; // Retrieves the cumulative number of users over time - -export const daily_unique_users_by_coin = '/hyperliquid/daily_unique_users_by_coin'; - -export const cumulative_inflow = '/hyperliquid/cumulative_inflow'; // Retrieves the cumulative inflow of funds over time. -export const open_interest = '/hyperliquid/open_interest'; // Retrieves the open interest data. -export const funding_rate = '/hyperliquid/funding_rate'; // Retrieves the funding rate data. -export const cumulative_new_users = '/hyperliquid/cumulative_new_users'; // Retrieves the cumulative number of unique users over time. -export const daily_inflow = '/hyperliquid/daily_inflow'; // Retrieves the daily inflow of funds. -export const liquidity_per_symbol = '/hyperliquid/liquidity_per_symbol'; // Retrieves the liquidity data per symbol. -export const largest_users_by_usd_volume = '/hyperliquid/largest_users_by_usd_volume'; // Retrieves the largest users by USD trading volume. -export const largest_user_depositors = '/hyperliquid/largest_user_depositors'; // Retrieves the largest user depositors. -export const largest_liquidated_notional_by_user = - '/hyperliquid/largest_liquidated_notional_by_user'; // Retrieves the largest liquidated notional by user. -export const largest_user_trade_count = '/hyperliquid/largest_user_trade_count'; // Retrieves the users with the highest trade counts. - -export const daily_notional_liquidated_total = '/hyperliquid/daily_notional_liquidated_total'; +export const total_users = 'total_users'; // Retrieves the total number of users. +export const total_usd_volume = 'total_usd_volume'; // Retrieves the total USD trading volume. +export const total_deposits = 'total_deposits'; // Retrieves the total amount of deposits. +export const total_withdrawals = 'total_withdrawals'; // Retrieves the total amount of withdrawals. +export const total_notional_liquidated = 'total_notional_liquidated'; // Retrieves the total notional value liquidated. +export const cumulative_usd_volume = 'cumulative_usd_volume'; // Retrieves the cumulative USD trading volume over time. +export const daily_usd_volume = 'daily_usd_volume'; // Retrieves the daily USD trading volume over time. +export const daily_usd_volume_by_coin = 'daily_usd_volume_by_coin'; // Retrieves the daily USD trading volume by coin over time. +export const daily_usd_volume_by_crossed = 'daily_usd_volume_by_crossed'; // Retrieves the daily USD trading volume by crossed over time. +export const daily_usd_volume_by_user = 'daily_usd_volume_by_user'; // Retrieves the daily USD trading volume by top 10 user and the rest are summed and marked as other. + +export const cumulative_trades = 'cumulative_trades'; // Retrieves the cumulative number of trades over time. The line chart of Cumulative total trades chart. + +export const daily_trades = 'daily_trades'; // Retrieves the daily number of trades. +export const daily_trades_by_coin = 'daily_trades_by_coin'; // Retrieves the daily number of trades by coin. +export const daily_trades_by_crossed = 'daily_trades_by_crossed'; // Retrieves the daily number of trades by crossed. +export const daily_trades_by_user = 'daily_trades_by_user'; // Retrieves the daily number of trades by top 10 user and the rest are summed and marked as other. +export const user_pnl = 'user_pnl'; // Retrieves the profit and loss (PnL) for all users daily. +export const cumulative_user_pnl = 'cumulative_user_pnl'; // Retrieves the cumulative PnL for all users over time. +export const hlp_liquidator_pnl = 'hlp_liquidator_pnl'; // Retrieves the PnL for liquidators daily. +export const hlp_liquidator_pnl_false = 'hlp_liquidator_pnl?is_hlp=false'; // Retrieves the PnL for liquidators daily. + +export const cumulative_hlp_liquidator_pnl = 'cumulative_hlp_liquidator_pnl'; // Retrieves the cumulative PnL for liquidators over time. + +export const cumulative_hlp_liquidator_pnl_false = 'cumulative_hlp_liquidator_pnl?is_hlp=false'; // Retrieves the cumulative PnL for liquidators over time. + +export const cumulative_liquidated_notional = 'cumulative_liquidated_notional'; // Retrieves the cumulative liquidated notional value over time. +export const daily_unique_users = 'daily_unique_users'; // Retrieves the daily number of unique users. +export const cumulative_users = 'cumulative_users'; // Retrieves the cumulative number of users over time + +export const daily_unique_users_by_coin = 'daily_unique_users_by_coin'; + +export const cumulative_inflow = 'cumulative_inflow'; // Retrieves the cumulative inflow of funds over time. +export const open_interest = 'open_interest'; // Retrieves the open interest data. +export const funding_rate = 'funding_rate'; // Retrieves the funding rate data. +export const cumulative_new_users = 'cumulative_new_users'; // Retrieves the cumulative number of unique users over time. +export const daily_inflow = 'daily_inflow'; // Retrieves the daily inflow of funds. +export const liquidity_per_symbol = 'liquidity_per_symbol'; // Retrieves the liquidity data per symbol. +export const largest_users_by_usd_volume = 'largest_users_by_usd_volume'; // Retrieves the largest users by USD trading volume. +export const largest_user_depositors = 'largest_user_depositors'; // Retrieves the largest user depositors. +export const largest_liquidated_notional_by_user = 'largest_liquidated_notional_by_user'; // Retrieves the largest liquidated notional by user. +export const largest_user_trade_count = 'largest_user_trade_count'; // Retrieves the users with the highest trade counts. + +export const daily_notional_liquidated_total = 'daily_notional_liquidated_total'; export const daily_notional_liquidated_by_leverage_type = - '/hyperliquid/daily_notional_liquidated_by_leverage_type'; + 'daily_notional_liquidated_by_leverage_type'; -export const liquidity_by_coin = '/hyperliquid/liquidity_by_coin'; -export const daily_notional_liquidated_by_coin = '/hyperliquid/daily_notional_liquidated_by_coin'; +export const liquidity_by_coin = 'liquidity_by_coin'; +export const daily_notional_liquidated_by_coin = 'daily_notional_liquidated_by_coin'; -export const total_accrued_fees = '/hyperliquid/total_accrued_fees'; -export const total_volume = '/hyperliquid/total_volume'; -export const hlp_positions = '/hyperliquid/hlp_positions'; -export const asset_ctxs = '/hyperliquid/asset_ctxs'; +export const total_accrued_fees = 'total_accrued_fees'; +export const total_volume = 'total_volume'; +export const hlp_positions = 'hlp_positions'; +export const asset_ctxs = 'asset_ctxs'; diff --git a/hooks/useRequest.ts b/hooks/useRequest.ts index 7ccc795..3d682bf 100644 --- a/hooks/useRequest.ts +++ b/hooks/useRequest.ts @@ -1,53 +1,44 @@ -import { useState, useEffect, useContext } from 'react'; +import { useState, useEffect, useContext, useCallback } from 'react'; import { DataContext } from '../contexts/data'; const fetcher = (url: string) => fetch(url).then((res) => res.json()); export function useRequest(url: string, defaultValue: any, key?: string, dontRefetch?: boolean) { const [loading, setLoading] = useState(true); - const [error, setError] = useState(); const [data, setData] = useState(defaultValue); const dataContext = useContext(DataContext); - const urlHasParams = url.indexOf('?') !== -1; - let params = ''; - if (dataContext.dates.from) { - if (!urlHasParams) { - params += '?'; - } - if (urlHasParams) { - params += '&'; - } - params += `start_date=${dataContext.dates.from}`; - } - if (dataContext.dates.to) { - params += `&end_date=${dataContext.dates.to}`; - } - - const init = async () => { - try { - setLoading(true); - const data = await fetcher(`${process.env.NEXT_PUBLIC_API_URL}${url}${params}`); - if (key && data[key]) { - setData(data[key]); - } else { - setData(data); - } - } catch (error) { - console.error(error); - setError(error); - } + const init = useCallback(async () => { + setLoading(true); + const data = await fetcher(`${process.env.NEXT_PUBLIC_DAT_URL}/${url}`); + const dataFromKey = key ? data[key] : data?.table_data || data?.chart_data || data; + setData( + dataFromKey.filter + ? dataFromKey.filter((line: any) => { + if (!line.time) { + return true; + } + if (dataContext.dates.from && new Date(line.time) < new Date(dataContext.dates.from)) { + return false; + } + if (dataContext.dates.to && new Date(line.time) > new Date(dataContext.dates.to)) { + return false; + } + return true; + }) + : dataFromKey + ); setLoading(false); - }; + }, [dataContext.dates.from, dataContext.dates.to, key, url]); useEffect(() => { init(); - }, [url]); + }, [init, url]); useEffect(() => { if (dontRefetch) return; init(); - }, [dataContext.dates.from, dataContext.dates.to]); + }, [dataContext.dates.from, dataContext.dates.to, dontRefetch, init]); - return [data, loading, error]; + return [data, loading]; } diff --git a/next.config.js b/next.config.js index 970af9d..4f2be75 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,7 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + output: 'export', reactStrictMode: false, webpack: (config, { isServer, defaultLoaders }) => { config.module.rules.push({ From 05e2bc926760b15b62454c6335ecb77b10e5ead0 Mon Sep 17 00:00:00 2001 From: Pascal Mugnier Date: Tue, 9 Jul 2024 10:36:35 +0200 Subject: [PATCH 58/59] Add concurrent requests limit (#35) Co-authored-by: Pascal Mugnier --- hooks/useRequest.ts | 65 +++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/hooks/useRequest.ts b/hooks/useRequest.ts index 3d682bf..5ec86a6 100644 --- a/hooks/useRequest.ts +++ b/hooks/useRequest.ts @@ -1,6 +1,9 @@ import { useState, useEffect, useContext, useCallback } from 'react'; import { DataContext } from '../contexts/data'; +let runningRequests: number = 0; +const MAX_CONCURRENT_REQUESTS = 4; + const fetcher = (url: string) => fetch(url).then((res) => res.json()); export function useRequest(url: string, defaultValue: any, key?: string, dontRefetch?: boolean) { @@ -9,26 +12,48 @@ export function useRequest(url: string, defaultValue: any, key?: string, dontRef const dataContext = useContext(DataContext); const init = useCallback(async () => { - setLoading(true); - const data = await fetcher(`${process.env.NEXT_PUBLIC_DAT_URL}/${url}`); - const dataFromKey = key ? data[key] : data?.table_data || data?.chart_data || data; - setData( - dataFromKey.filter - ? dataFromKey.filter((line: any) => { - if (!line.time) { - return true; - } - if (dataContext.dates.from && new Date(line.time) < new Date(dataContext.dates.from)) { - return false; - } - if (dataContext.dates.to && new Date(line.time) > new Date(dataContext.dates.to)) { - return false; - } - return true; - }) - : dataFromKey - ); - setLoading(false); + const request = async () => { + if (runningRequests > MAX_CONCURRENT_REQUESTS) { + setTimeout(() => { + request(); + }, 500); + return; + } + + runningRequests++; + setLoading(true); + try { + const data = await fetcher(`${process.env.NEXT_PUBLIC_DAT_URL}/${url}`); + runningRequests--; + + const dataFromKey = key ? data[key] : data?.table_data || data?.chart_data || data; + setData( + dataFromKey.filter + ? dataFromKey.filter((line: any) => { + if (!line.time) { + return true; + } + if ( + dataContext.dates.from && + new Date(line.time) < new Date(dataContext.dates.from) + ) { + return false; + } + if (dataContext.dates.to && new Date(line.time) > new Date(dataContext.dates.to)) { + return false; + } + return true; + }) + : dataFromKey + ); + setLoading(false); + } catch (e) { + console.log(e); + runningRequests--; + } + }; + + request(); }, [dataContext.dates.from, dataContext.dates.to, key, url]); useEffect(() => { From ad62cf79a39d3a6e4efd0f596f2286ca79827bb7 Mon Sep 17 00:00:00 2001 From: Pascal Mugnier Date: Thu, 21 Nov 2024 15:24:29 +0100 Subject: [PATCH 59/59] Remove footer --- app/page.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/page.tsx b/app/page.tsx index 0676fcd..704b9ec 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -8,7 +8,6 @@ const Home: NextPage = () => { <>
-
); };