diff --git a/components/F2/UserDashboard.tsx b/components/F2/UserDashboard.tsx index 785509b4a..ebf8525c8 100644 --- a/components/F2/UserDashboard.tsx +++ b/components/F2/UserDashboard.tsx @@ -163,12 +163,13 @@ export const MonthlyRewards = ({ } -const NumberItem = ({ noDataFallback = '-', href = '', footer = undefined, isLoading = false, value = 0, price = undefined, label = '', isUsd = false, precision = 0 }) => { +const NumberItem = ({ noDataFallback = '-', href = '', footer = undefined, isLoading = false, value = 0, price = undefined, label = '', isUsd = false, precision = 0, isPerc = false }) => { return { isLoading ? : {!value ? noDataFallback : value > 100000 ? smartShortNumber(value, 2, isUsd) : preciseCommify(value, precision, isUsd)}{!!price && !!value ? ` (${smartShortNumber(value * price, 2, true)})` : ''} + {!!value && isPerc ? "%" : null} } { @@ -183,9 +184,9 @@ const NumberItem = ({ noDataFallback = '-', href = '', footer = undefined, isLoa } -export const NumberCard = ({ imageSrc = '', noDataFallback = undefined, href = undefined, footer = undefined, isLoading = false, value = 0, label = '', price = undefined, isUsd = false, precision = 0 }) => { +export const NumberCard = ({ imageSrc = '', noDataFallback = undefined, href = undefined, footer = undefined, isLoading = false, value = 0, label = '', price = undefined, isUsd = false, precision = 0, isPerc = false }) => { return - + } diff --git a/components/F2/liquidations/firm-positions.tsx b/components/F2/liquidations/firm-positions.tsx index 0c9f43869..bca3d834e 100644 --- a/components/F2/liquidations/firm-positions.tsx +++ b/components/F2/liquidations/firm-positions.tsx @@ -12,7 +12,7 @@ import { SkeletonBlob } from "@app/components/common/Skeleton"; import { SmallTextLoader } from "@app/components/common/Loaders/SmallTextLoader"; import { FirmPositionsTable } from "../Infos/FirmPositionsTable"; -const groupPositionsBy = (positions: any[], groupBy: string, attributeToSum: string) => { +export const groupPositionsBy = (positions: any[], groupBy: string, attributeToSum: string) => { return Object.entries( positions.reduce((prev, curr) => { return { ...prev, [curr[groupBy]]: (prev[curr[groupBy]] || 0) + curr[attributeToSum] }; diff --git a/components/Transparency/TransparencyTabs.tsx b/components/Transparency/TransparencyTabs.tsx index 780af7f4c..a457d8624 100644 --- a/components/Transparency/TransparencyTabs.tsx +++ b/components/Transparency/TransparencyTabs.tsx @@ -2,10 +2,10 @@ import { useAppTheme } from '@app/hooks/useAppTheme'; import { Tabs, TabList, Tab, VStack, Text, HStack, Image } from '@chakra-ui/react' import Link from '../common/Link'; -type TabsType = 'overview' | 'treasury' | 'veNfts' | 'liquidity' | 'inv' | 'dola' | 'dbr' | 'multisigs' | 'interest-model' | 'feds' | 'stabilizer' | 'dao' | 'shortfalls' | 'bad-debts'; +type TabsType = 'overview' | 'keymetrics' | 'treasury' | 'veNfts' | 'liquidity' | 'inv' | 'dola' | 'dbr' | 'multisigs' | 'interest-model' | 'feds' | 'stabilizer' | 'dao' | 'shortfalls' | 'bad-debts'; const TABS = [ - // { page: 'overview', label: 'Overview' }, + { page: 'keymetrics', label: 'Key Metrics' }, { page: 'treasury', label: 'Treasury' }, { page: 'veNfts', label: 'veNfts' }, { page: 'liquidity', label: 'Liquidity' }, diff --git a/next.config.js b/next.config.js index 768650404..5e4501788 100644 --- a/next.config.js +++ b/next.config.js @@ -119,8 +119,8 @@ const redirects = async () => { }, { source: '/transparency', - destination: '/transparency/overview', - permanent: true, + destination: '/transparency/keymetrics', + permanent: false, }, { source: '/banking', diff --git a/pages/firm/[market].tsx b/pages/firm/[market].tsx index bf8ff3ca2..408bdeb0c 100644 --- a/pages/firm/[market].tsx +++ b/pages/firm/[market].tsx @@ -21,7 +21,7 @@ import { FirmRewardWrapper } from '@app/components/F2/rewards/FirmRewardWrapper' import { CvxCrvPreferences } from '@app/components/F2/rewards/CvxCrvPreferences' import { DailyLimitCountdown } from '@app/components/common/Countdown' import Container from '@app/components/common/Container' -import { InfoMessage } from '@app/components/common/Messages' +import { InfoMessage, WarningMessage } from '@app/components/common/Messages' import { shortenNumber } from '@app/util/markets' import { preciseCommify } from '@app/util/misc' import { WorthEvoChartWrapper } from '@app/components/F2/WorthEvoChartContainer' @@ -136,7 +136,7 @@ export const F2MarketPage = ({ market }: { market: string }) => { { - isMultisig && { } +export const DolaBadDebtRepaymentProgressBar = ({ + progress, +}: { + progress: number +}) => { + const [isSmallerThan] = useMediaQuery('(max-width: 1150px)'); + return + + {!isSmallerThan && 0%} + + {progress > 0 && !isSmallerThan && {shortenNumber(progress, 2)}%} + { + !isSmallerThan ? + + : + + {shortenNumber(progress, 2)}% + + + } + + {!isSmallerThan! && 100%} + + +} + const transactionsColumns = [ { field: 'symbol', @@ -353,8 +379,7 @@ export const BadDebtPage = () => { const { prices } = usePrices(); const [selected, setSelected] = useState('all'); const isAllCase = selected === 'all'; - const isDolaCase = selected.toLowerCase().includes('dola'); - const [isSmallerThan] = useMediaQuery('(max-width: 1150px)') + const isDolaCase = selected.toLowerCase().includes('dola'); const dolaPrice = !!prices ? prices['dola-usd']?.usd : 1; const totalDirectRepaymentsForTable = totalRepaymentKeys.map(key => { @@ -452,24 +477,7 @@ export const BadDebtPage = () => { - - - {!isSmallerThan && 0%} - - {dolaBadDebtRepaidProgress > 0 && !isSmallerThan && {shortenNumber(dolaBadDebtRepaidProgress, 2)}%} - { - !isSmallerThan ? - - : - - {shortenNumber(dolaBadDebtRepaidProgress, 2)}% - - - } - - {!isSmallerThan! && 100%} - - + { barProps={{ eventName: 'Repayment' }} areaProps={{ id: 'bad-debt-chart', showRangeBtns: true, defaultRange: '1Y', rangesToInclude: ['All', 'YTD', '1Y', '6M', '3M', '7D'], yLabel: 'DOLA bad debt', useRecharts: true, fillInByDayInterval: 1, simplifyData: false, showEvents: true, showEventsLabels: true, domainYpadding: 1000000, showMaxY: false, showTooltips: true, autoMinY: true, mainColor: 'info', allowZoom: true }} /> - { - return +export const DolaBackingLegend = (props) => { + return { LEGEND_ITEMS.map((item, index) => { return @@ -52,23 +52,16 @@ const Legend = () => { }) } - + } -export const DolaDiagram = () => { - const { themeStyles } = useAppTheme(); - const { dolaSupplies } = useDAO(); - const { markets, isLoading } = useDBRMarkets(); - const { fedOverviews, isLoading: isLoadingOverview } = useFedOverview(); - const { data, isLoading: isLoadingRepayments } = useRepayments(); - const { prices } = usePrices(['velodrome-finance']); - - const fedsPieChartData = fedOverviews.map(f => { +export const fedsDataToPieChart = (fedOverviews: any[], colors) => { + return fedOverviews.map(f => { const name = f.type === 'AMM' ? `${f.name} ` + (f.subBalances.reduce((acc, curr) => acc ? acc + '-' + curr.symbol : curr.symbol, '') + ' LP') : f.name; const balance = ['FiRM', 'Frontier'].includes(f.protocol) ? f.borrows : f.supply; - const color = ['Frontier', 'Fuse'].includes(f.protocol) ? themeStyles.colors.error : f.protocol === 'FiRM' ? themeStyles.colors.success : themeStyles.colors.info; + const color = ['Frontier', 'Fuse'].includes(f.protocol) ? colors.error : f.protocol === 'FiRM' ? colors.success : colors.info; return { ...f, token: { symbol: name }, @@ -81,6 +74,17 @@ export const DolaDiagram = () => { textColor: color, } }).filter(d => d.sliceValue > 0); +} + +export const DolaDiagram = () => { + const { themeStyles } = useAppTheme(); + const { dolaSupplies } = useDAO(); + const { markets, isLoading } = useDBRMarkets(); + const { fedOverviews, isLoading: isLoadingOverview } = useFedOverview(); + const { data, isLoading: isLoadingRepayments } = useRepayments(); + const { prices } = usePrices(['velodrome-finance']); + + const fedsPieChartData = fedsDataToPieChart(fedOverviews, themeStyles?.colors); const firmPieChartData = markets.map(f => { return { @@ -164,7 +168,7 @@ export const DolaDiagram = () => { } /> - + void, + value: boolean, + label?: string +}) => + + {label} + + setter(!value)} isChecked={value} id={id} /> + + +const above100UsdFilter = (item) => item.balance * (item.price || item.usdPrice) >= 100; + +export const KeymetricsPage = () => { + const { themeName, themeStyles } = useAppTheme(); + const { prices, isLoading: isLoadingPrices } = usePricesV2(true); + const { price: dolaPrice, isLoading: isDolaPriceLoading } = useDOLAPrice(); + const { positions, userPositions, isLoading: isLoadingPositions } = useFirmUsers(); + const { treasury, anchorReserves, multisigs, isLoading: isLoadingDao } = useDAO(); + const { fedOverviews, isLoading: isLoadingOverview } = useFedOverview(); + const { data: currentCirculatingSupply } = useCustomSWR(`/api/dola/circulating-supply`); + const { data: invCirculatingSupply } = useCustomSWR(`/api/inv/circulating-supply`); + const invMarketCap = prices["inverse-finance"]?.usd * invCirculatingSupply; + const [excludeOwnTokens, setExcludeOwnTokens] = useState(false); + + const totalMultisigs = multisigs?.map(m => { + return { label: m.shortName, balance: getFundsTotalUsd(m.funds.filter(above100UsdFilter), prices, 'balance'), onlyUsdValue: true, usdPrice: 1, drill: m.funds } + }); + + const totalHoldings = [ + { label: 'Treasury Contract', balance: getFundsTotalUsd(treasury, prices, 'balance'), onlyUsdValue: true, usdPrice: 1, drill: treasury }, + { label: 'Frontier Reserves', balance: getFundsTotalUsd(anchorReserves, prices, 'balance'), onlyUsdValue: true, usdPrice: 1, drill: anchorReserves }, + { label: 'veNFTs', balance: getFundsTotalUsd(multisigs?.map(m => m.funds.filter(fund => !!fund.token.veNftId)), prices, 'balance'), onlyUsdValue: true, usdPrice: 1, drill: totalMultisigs }, + { label: 'Multisigs (excl. veNFTs)', balance: getFundsTotalUsd(multisigs?.map(m => m.funds.filter(fund => !fund.token.veNftId)), prices, 'balance'), onlyUsdValue: true, usdPrice: 1, drill: totalMultisigs }, + ]; + + const totalHoldingsExcludeOwnTokens = [ + { label: 'Treasury Contract', balance: getFundsTotalUsd(treasury.filter(t => !OWN_TOKENS.includes(t.token.symbol)), prices, 'balance'), onlyUsdValue: true, usdPrice: 1, drill: treasury }, + { label: 'Frontier Reserves', balance: getFundsTotalUsd(anchorReserves, prices, 'balance'), usdPrice: 1, drill: anchorReserves }, + { label: 'veNFTs', balance: getFundsTotalUsd(multisigs?.map(m => m.funds.filter(fund => !!fund.token.veNftId)), prices, 'balance'), onlyUsdValue: true, usdPrice: 1, drill: totalMultisigs }, + { label: 'Multisigs (excl. veNFTs)', balance: getFundsTotalUsd(multisigs?.map(m => m.funds.filter(fund => !fund.token.veNftId).filter(t => !OWN_TOKENS.includes(t.token.symbol))), prices, 'balance'), onlyUsdValue: true, usdPrice: 1, drill: totalMultisigs }, + ]; + + const pieSize = 300; + + const totalTvl = positions.reduce((prev, curr) => prev + (curr.deposits * curr.market.price), 0); + const totalDebt = positions.reduce((prev, curr) => prev + curr.debt, 0); + const nbUsers = userPositions.length; + const nbBorrowers = userPositions.filter(p => p.debt > 0).length; + const nbStakers = userPositions.filter(p => p.stakedInv > 0).length; + + const positionsWithDebt = positions.filter(p => p.debt > 0); + const positionsWithDeposits = positions.filter(p => p.deposits > 0); + const groupMarketsByDeposits = groupPositionsBy(positionsWithDeposits, 'marketName', 'tvl'); + const groupMarketsByDebt = groupPositionsBy(positionsWithDebt, 'marketName', 'debt'); + + const isLoading = isLoadingDao || isLoadingPrices || isDolaPriceLoading || isLoadingPositions; + const mainFontSize = { base: '16px', sm: '20px', md: '26px' }; + const dashboardCardTitleProps = { w: 'fit-content', position: 'static', fontSize: mainFontSize, fontWeight: 'extrabold' }; + const dashboardCardProps = { direction: 'column', mx: '0', w: { base: '100vw', sm: '95vw', lg: '600px' }, borderRadius: { base: '0', sm: '8' } }; + + const fedsPieChartData = fedsDataToPieChart(fedOverviews, themeStyles?.colors); + + const dolaBackingProps = { + dataKey: "sliceValue", + nameKey: "sliceName", + activeFill: 'keep', + asStable: true, + type: 'balance', + useRecharts: true, + isLoading, + w: 'full', + leftSideMaxW: '300px', + chartProps: { activeFill: 'keep', centralFill: themeStyles.colors.mainTextColor, isUsd: false } + }; + + return ( + + + Inverse Finance - Transparency Treasury + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default KeymetricsPage diff --git a/variables/menus.ts b/variables/menus.ts index b814a3b96..190e56186 100644 --- a/variables/menus.ts +++ b/variables/menus.ts @@ -100,12 +100,12 @@ export const MENUS = { }, { label: 'Transparency', - href: '/transparency/treasury', + href: '/transparency/keymetrics', submenus: [ - // { - // href: '/transparency/overview', - // label: 'Overview', - // }, + { + href: '/transparency/keymetrics', + label: 'Key Metrics', + }, { href: '/transparency/treasury', label: 'Treasury',