From 858925d023bbf3166723d8477aa234631ac8cb2f Mon Sep 17 00:00:00 2001 From: Gleiser Oliveira Date: Tue, 26 May 2026 19:23:58 -0600 Subject: [PATCH 1/8] feat: add Stats - Overview --- apps/evm/src/pages/Stats/TopWallets/TopWalletsTable/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/evm/src/pages/Stats/TopWallets/TopWalletsTable/index.tsx b/apps/evm/src/pages/Stats/TopWallets/TopWalletsTable/index.tsx index 8d03eb0ad6..e124e1255e 100644 --- a/apps/evm/src/pages/Stats/TopWallets/TopWalletsTable/index.tsx +++ b/apps/evm/src/pages/Stats/TopWallets/TopWalletsTable/index.tsx @@ -360,6 +360,7 @@ export const TopWalletsTable: React.FC = ({ kind, limit = const meta = marketMetaByAddress[market]; const label = meta?.symbol ?? truncateAddress(market); const bgColor = colorByMarket[market] ?? theme.colors.grey; + return (
Date: Tue, 26 May 2026 19:23:58 -0600 Subject: [PATCH 2/8] feat: add Stats - Overview --- apps/evm/src/pages/Stats/index.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/evm/src/pages/Stats/index.tsx b/apps/evm/src/pages/Stats/index.tsx index ccc90a269a..2ef3276835 100644 --- a/apps/evm/src/pages/Stats/index.tsx +++ b/apps/evm/src/pages/Stats/index.tsx @@ -1,10 +1,13 @@ import { Page, Tabs } from 'components'; import { useTranslation } from 'libs/translations'; +import { EModeTable } from './EModeTable'; import { Header } from './Header'; import { HistoricalDominanceChart } from './HistoricalDominanceChart'; import { HistoricalLiquidityChart } from './HistoricalLiquidityChart'; import { HistoricalMarketChart } from './HistoricalMarketChart'; import { MarketKpis } from './MarketKpis'; +import { MarketsTable } from './MarketsTable'; +import { RiskParametersTable } from './RiskParametersTable'; import { TopWallets } from './TopWallets'; import { TransactionsVolume } from './TransactionsVolume'; import { WalletKpis } from './WalletKpis'; @@ -27,6 +30,15 @@ const Overview: React.FC = () => (
); +const Markets: React.FC = () => ( +
+ + + + +
+); + const Stats: React.FC = () => { const { t } = useTranslation(); @@ -39,7 +51,7 @@ const Stats: React.FC = () => { navType="searchParam" tabs={[ { id: 'overview', title: t('statsPage.tabs.overview'), content: }, - { id: 'markets', title: t('statsPage.tabs.markets'), content: null }, + { id: 'markets', title: t('statsPage.tabs.markets'), content: }, { id: 'wallets', title: t('statsPage.tabs.wallets'), content: null }, { id: 'liquidations', title: t('statsPage.tabs.liquidations'), content: null }, ]} From e226ec20b7001f591e1d25cc9e398001d01d7b39 Mon Sep 17 00:00:00 2001 From: Gleiser Oliveira Date: Wed, 27 May 2026 20:32:54 -0600 Subject: [PATCH 3/8] chore: add changeset --- .changeset/angry-experts-grow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/angry-experts-grow.md diff --git a/.changeset/angry-experts-grow.md b/.changeset/angry-experts-grow.md new file mode 100644 index 0000000000..948f4f3a4b --- /dev/null +++ b/.changeset/angry-experts-grow.md @@ -0,0 +1,5 @@ +--- +"@venusprotocol/evm": minor +--- + +Add Stats page - Markets tab From cccfb7e047a8085ce809298755abc962049b7f0c Mon Sep 17 00:00:00 2001 From: Gleiser Oliveira Date: Wed, 27 May 2026 20:33:23 -0600 Subject: [PATCH 4/8] feat: add getRiskDashboardMarkets and getRiskDashboardEModePools --- apps/evm/src/clients/api/index.ts | 4 ++ .../clients/api/queries/fetchRiskDashboard.ts | 35 +++++++++++ .../getRiskDashboardEModePools/index.ts | 26 ++++++++ .../useGetRiskDashboardEModePools.ts | 29 +++++++++ .../queries/getRiskDashboardMarkets/index.ts | 63 +++++++++++++++++++ .../useGetRiskDashboardMarkets.ts | 29 +++++++++ apps/evm/src/constants/functionKey.ts | 2 + 7 files changed, 188 insertions(+) create mode 100644 apps/evm/src/clients/api/queries/fetchRiskDashboard.ts create mode 100644 apps/evm/src/clients/api/queries/getRiskDashboardEModePools/index.ts create mode 100644 apps/evm/src/clients/api/queries/getRiskDashboardEModePools/useGetRiskDashboardEModePools.ts create mode 100644 apps/evm/src/clients/api/queries/getRiskDashboardMarkets/index.ts create mode 100644 apps/evm/src/clients/api/queries/getRiskDashboardMarkets/useGetRiskDashboardMarkets.ts diff --git a/apps/evm/src/clients/api/index.ts b/apps/evm/src/clients/api/index.ts index 39885cb11a..c7db8e59d3 100644 --- a/apps/evm/src/clients/api/index.ts +++ b/apps/evm/src/clients/api/index.ts @@ -322,6 +322,10 @@ export * from './queries/getTradeReduceSwapQuotes/useGetTradeReduceSwapQuotes'; export * from './queries/getRiskDashboardMarketAggregates'; export * from './queries/getRiskDashboardMarketAggregates/useGetRiskDashboardMarketAggregates'; +export * from './queries/getRiskDashboardMarkets'; +export * from './queries/getRiskDashboardMarkets/useGetRiskDashboardMarkets'; +export * from './queries/getRiskDashboardEModePools'; +export * from './queries/getRiskDashboardEModePools/useGetRiskDashboardEModePools'; export * from './queries/getRiskDashboardMarketSnapshots'; export * from './queries/getRiskDashboardMarketSnapshots/useGetRiskDashboardMarketSnapshots'; export * from './queries/getRiskDashboardWalletAggregates'; diff --git a/apps/evm/src/clients/api/queries/fetchRiskDashboard.ts b/apps/evm/src/clients/api/queries/fetchRiskDashboard.ts new file mode 100644 index 0000000000..6809dd5b17 --- /dev/null +++ b/apps/evm/src/clients/api/queries/fetchRiskDashboard.ts @@ -0,0 +1,35 @@ +import { VError } from 'libs/errors'; +import type { ChainId } from 'types'; +import { restService } from 'utilities'; + +export async function fetchRiskDashboard({ + endpoint, + chainId, + params, +}: { + endpoint: string; + chainId: ChainId; + params?: Record; +}) { + const response = await restService({ + endpoint, + method: 'GET', + params: { chainId, ...params }, + }); + + const payload = response.data; + + if (payload && 'error' in payload) { + throw new VError({ + type: 'unexpected', + code: 'somethingWentWrong', + data: { exception: payload.error }, + }); + } + + if (!payload) { + throw new VError({ type: 'unexpected', code: 'somethingWentWrong' }); + } + + return payload; +} diff --git a/apps/evm/src/clients/api/queries/getRiskDashboardEModePools/index.ts b/apps/evm/src/clients/api/queries/getRiskDashboardEModePools/index.ts new file mode 100644 index 0000000000..d2eba02e5a --- /dev/null +++ b/apps/evm/src/clients/api/queries/getRiskDashboardEModePools/index.ts @@ -0,0 +1,26 @@ +import type { ChainId } from 'types'; +import type { Address } from 'viem'; +import { fetchRiskDashboard } from '../fetchRiskDashboard'; + +export interface ApiRiskDashboardEModeSetting { + poolId: string; + label: string; + isActive: boolean; + marketAddress: Address; + collateralFactorMantissa: string; + liquidationThresholdMantissa: string; + liquidationIncentiveMantissa: string; + canBeCollateral: boolean; + isBorrowable: boolean; +} + +export interface GetRiskDashboardEModePoolsResponse { + chainId: string; + settings: ApiRiskDashboardEModeSetting[]; +} + +export const getRiskDashboardEModePools = ({ chainId }: { chainId: ChainId }) => + fetchRiskDashboard({ + endpoint: '/risk-dashboard/emode-pools', + chainId, + }); diff --git a/apps/evm/src/clients/api/queries/getRiskDashboardEModePools/useGetRiskDashboardEModePools.ts b/apps/evm/src/clients/api/queries/getRiskDashboardEModePools/useGetRiskDashboardEModePools.ts new file mode 100644 index 0000000000..40a752a7f0 --- /dev/null +++ b/apps/evm/src/clients/api/queries/getRiskDashboardEModePools/useGetRiskDashboardEModePools.ts @@ -0,0 +1,29 @@ +import { type QueryObserverOptions, useQuery } from '@tanstack/react-query'; + +import FunctionKey from 'constants/functionKey'; +import { useChainId } from 'libs/wallet'; +import type { ChainId } from 'types'; +import { type GetRiskDashboardEModePoolsResponse, getRiskDashboardEModePools } from '.'; + +export type UseGetRiskDashboardEModePoolsQueryKey = [ + FunctionKey.GET_RISK_DASHBOARD_EMODE_POOLS, + { chainId: ChainId }, +]; + +type Options = QueryObserverOptions< + GetRiskDashboardEModePoolsResponse, + Error, + GetRiskDashboardEModePoolsResponse, + GetRiskDashboardEModePoolsResponse, + UseGetRiskDashboardEModePoolsQueryKey +>; + +export const useGetRiskDashboardEModePools = (options?: Partial) => { + const { chainId } = useChainId(); + + return useQuery({ + queryKey: [FunctionKey.GET_RISK_DASHBOARD_EMODE_POOLS, { chainId }], + queryFn: () => getRiskDashboardEModePools({ chainId }), + ...options, + }); +}; diff --git a/apps/evm/src/clients/api/queries/getRiskDashboardMarkets/index.ts b/apps/evm/src/clients/api/queries/getRiskDashboardMarkets/index.ts new file mode 100644 index 0000000000..e779483bfb --- /dev/null +++ b/apps/evm/src/clients/api/queries/getRiskDashboardMarkets/index.ts @@ -0,0 +1,63 @@ +import { VError } from 'libs/errors'; +import type { ChainId } from 'types'; +import { restService } from 'utilities'; +import type { Address } from 'viem'; +import type { ApiRiskDashboardAsOf } from '../getRiskDashboardMarketAggregates'; + +export interface ApiRiskDashboardMarket { + marketAddress: Address; + underlyingAddress: Address | null; + vTokenDecimals: number; + totalSupplyUsdCents: string; + totalBorrowsUsdCents: string; + liquidityUsdCents: string; + supplyCapUsdCents: string | null; + borrowCapUsdCents: string | null; + priceMantissa: string; + supplyRatePerBlockMantissa: string; + borrowRatePerBlockMantissa: string; + collateralFactorMantissa: string; + reserveFactorMantissa: string; + liquidationThresholdMantissa: string; + liquidationIncentiveMantissa: string; + topSupplierUsdCents: string | null; + topBorrowerUsdCents: string | null; + totalDebtAgainstUsdCents: string | null; + stablecoinDebtUsdCents: string | null; + bnbDebtUsdCents: string | null; + otherDebtUsdCents: string | null; +} + +export interface GetRiskDashboardMarketsInput { + chainId: ChainId; +} + +export interface GetRiskDashboardMarketsResponse { + chainId: string; + asOf: ApiRiskDashboardAsOf | null; + markets: ApiRiskDashboardMarket[]; +} + +export async function getRiskDashboardMarkets({ chainId }: GetRiskDashboardMarketsInput) { + const response = await restService({ + endpoint: '/risk-dashboard/markets', + method: 'GET', + params: { chainId }, + }); + + const payload = response.data; + + if (payload && 'error' in payload) { + throw new VError({ + type: 'unexpected', + code: 'somethingWentWrong', + data: { exception: payload.error }, + }); + } + + if (!payload) { + throw new VError({ type: 'unexpected', code: 'somethingWentWrong' }); + } + + return payload; +} diff --git a/apps/evm/src/clients/api/queries/getRiskDashboardMarkets/useGetRiskDashboardMarkets.ts b/apps/evm/src/clients/api/queries/getRiskDashboardMarkets/useGetRiskDashboardMarkets.ts new file mode 100644 index 0000000000..b91707789c --- /dev/null +++ b/apps/evm/src/clients/api/queries/getRiskDashboardMarkets/useGetRiskDashboardMarkets.ts @@ -0,0 +1,29 @@ +import { type QueryObserverOptions, useQuery } from '@tanstack/react-query'; + +import FunctionKey from 'constants/functionKey'; +import { useChainId } from 'libs/wallet'; +import type { ChainId } from 'types'; +import { type GetRiskDashboardMarketsResponse, getRiskDashboardMarkets } from '.'; + +export type UseGetRiskDashboardMarketsQueryKey = [ + FunctionKey.GET_RISK_DASHBOARD_MARKETS, + { chainId: ChainId }, +]; + +type Options = QueryObserverOptions< + GetRiskDashboardMarketsResponse, + Error, + GetRiskDashboardMarketsResponse, + GetRiskDashboardMarketsResponse, + UseGetRiskDashboardMarketsQueryKey +>; + +export const useGetRiskDashboardMarkets = (options?: Partial) => { + const { chainId } = useChainId(); + + return useQuery({ + queryKey: [FunctionKey.GET_RISK_DASHBOARD_MARKETS, { chainId }], + queryFn: () => getRiskDashboardMarkets({ chainId }), + ...options, + }); +}; diff --git a/apps/evm/src/constants/functionKey.ts b/apps/evm/src/constants/functionKey.ts index 453d964e2e..bfef92040d 100644 --- a/apps/evm/src/constants/functionKey.ts +++ b/apps/evm/src/constants/functionKey.ts @@ -101,6 +101,8 @@ enum FunctionKey { GET_TOKEN_PAIR_K_LINE_CANDLES = 'GET_TOKEN_PAIR_K_LINE_CANDLES', GET_TRADE_REDUCE_SWAP_QUOTES = 'GET_TRADE_REDUCE_SWAP_QUOTES', GET_RISK_DASHBOARD_MARKET_AGGREGATES = 'GET_RISK_DASHBOARD_MARKET_AGGREGATES', + GET_RISK_DASHBOARD_MARKETS = 'GET_RISK_DASHBOARD_MARKETS', + GET_RISK_DASHBOARD_EMODE_POOLS = 'GET_RISK_DASHBOARD_EMODE_POOLS', GET_RISK_DASHBOARD_MARKET_SNAPSHOTS = 'GET_RISK_DASHBOARD_MARKET_SNAPSHOTS', GET_RISK_DASHBOARD_WALLET_AGGREGATES = 'GET_RISK_DASHBOARD_WALLET_AGGREGATES', GET_RISK_DASHBOARD_TOP_WALLETS = 'GET_RISK_DASHBOARD_TOP_WALLETS', From 29546e39470c21c4d36c8141a9092e71f3e8aa22 Mon Sep 17 00:00:00 2001 From: Gleiser Oliveira Date: Wed, 27 May 2026 20:36:07 -0600 Subject: [PATCH 5/8] feat: add EModeTable --- apps/evm/src/pages/Stats/EModeTable/index.tsx | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 apps/evm/src/pages/Stats/EModeTable/index.tsx diff --git a/apps/evm/src/pages/Stats/EModeTable/index.tsx b/apps/evm/src/pages/Stats/EModeTable/index.tsx new file mode 100644 index 0000000000..67be44bf4b --- /dev/null +++ b/apps/evm/src/pages/Stats/EModeTable/index.tsx @@ -0,0 +1,148 @@ +import { type ApiRiskDashboardEModeSetting, useGetRiskDashboardEModePools } from 'clients/api'; +import { Spinner, Table, type TableColumn, TokenIconWithSymbol } from 'components'; +import { useGetVTokens } from 'libs/tokens/hooks/useGetVTokens'; +import { useTranslation } from 'libs/translations'; +import { useMemo } from 'react'; +import type { Token } from 'types'; +import { convertPercentageFromSmartContract, formatPercentageToReadableValue } from 'utilities'; +import { getAddress } from 'viem'; + +interface EModeRow extends ApiRiskDashboardEModeSetting { + underlyingToken: Token | null; +} + +const sortByMantissa = (a: string, b: string, direction: 'asc' | 'desc') => { + const aValue = convertPercentageFromSmartContract(a); + const bValue = convertPercentageFromSmartContract(b); + return direction === 'asc' ? aValue - bValue : bValue - aValue; +}; + +export const EModeTable: React.FC = () => { + const { t } = useTranslation(); + const vTokens = useGetVTokens(); + const { data, isLoading, isError } = useGetRiskDashboardEModePools(); + + const underlyingByVToken = useMemo(() => { + const map = new Map(); + for (const vToken of vTokens) { + map.set(getAddress(vToken.address), vToken.underlyingToken); + } + return map; + }, [vTokens]); + + const rows = useMemo(() => { + if (!data) { + return []; + } + return data.settings.map(setting => ({ + ...setting, + underlyingToken: underlyingByVToken.get(setting.marketAddress) ?? null, + })); + }, [data, underlyingByVToken]); + + const columns = useMemo[]>( + () => [ + { + key: 'group', + label: t('statsPage.eModeTable.columns.group'), + selectOptionLabel: t('statsPage.eModeTable.columns.group'), + align: 'left', + renderCell: row => row.label, + sortRows: (a, b, direction) => + direction === 'asc' ? a.label.localeCompare(b.label) : b.label.localeCompare(a.label), + }, + { + key: 'asset', + label: t('statsPage.eModeTable.columns.asset'), + selectOptionLabel: t('statsPage.eModeTable.columns.asset'), + align: 'left', + renderCell: row => + row.underlyingToken ? ( + + ) : ( + row.marketAddress + ), + }, + { + key: 'collateralFactor', + label: t('statsPage.eModeTable.columns.collateralFactor'), + selectOptionLabel: t('statsPage.eModeTable.columns.collateralFactor'), + align: 'right', + renderCell: row => + formatPercentageToReadableValue( + convertPercentageFromSmartContract(row.collateralFactorMantissa), + ), + sortRows: (a, b, direction) => + sortByMantissa(a.collateralFactorMantissa, b.collateralFactorMantissa, direction), + }, + { + key: 'liquidationThreshold', + label: t('statsPage.eModeTable.columns.liquidationThreshold'), + selectOptionLabel: t('statsPage.eModeTable.columns.liquidationThreshold'), + align: 'right', + renderCell: row => + formatPercentageToReadableValue( + convertPercentageFromSmartContract(row.liquidationThresholdMantissa), + ), + sortRows: (a, b, direction) => + sortByMantissa(a.liquidationThresholdMantissa, b.liquidationThresholdMantissa, direction), + }, + { + key: 'liquidationIncentive', + label: t('statsPage.eModeTable.columns.liquidationIncentive'), + selectOptionLabel: t('statsPage.eModeTable.columns.liquidationIncentive'), + align: 'right', + renderCell: row => + formatPercentageToReadableValue( + convertPercentageFromSmartContract(row.liquidationIncentiveMantissa), + ), + sortRows: (a, b, direction) => + sortByMantissa(a.liquidationIncentiveMantissa, b.liquidationIncentiveMantissa, direction), + }, + { + key: 'collateral', + label: t('statsPage.eModeTable.columns.collateral'), + selectOptionLabel: t('statsPage.eModeTable.columns.collateral'), + align: 'right', + renderCell: row => + row.canBeCollateral ? t('statsPage.eModeTable.yes') : t('statsPage.eModeTable.no'), + }, + { + key: 'borrowable', + label: t('statsPage.eModeTable.columns.borrowable'), + selectOptionLabel: t('statsPage.eModeTable.columns.borrowable'), + align: 'right', + renderCell: row => + row.isBorrowable ? t('statsPage.eModeTable.yes') : t('statsPage.eModeTable.no'), + }, + ], + [t], + ); + + if (isLoading) { + return ( +
+ +
+ ); + } + + if (isError || !data) { + return

{t('statsPage.eModeTable.unavailable')}

; + } + + if (rows.length === 0) { + return

{t('statsPage.eModeTable.noData')}

; + } + + return ( + `${row.poolId}-${row.marketAddress}`} + minWidth="1000px" + tableLayout="auto" + /> + ); +}; From f177e71accc4fde73c5ce2a735b36687b0a01c5a Mon Sep 17 00:00:00 2001 From: Gleiser Oliveira Date: Wed, 27 May 2026 20:36:26 -0600 Subject: [PATCH 6/8] feat: add MarketsTable --- .../src/pages/Stats/MarketsTable/index.tsx | 314 ++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 apps/evm/src/pages/Stats/MarketsTable/index.tsx diff --git a/apps/evm/src/pages/Stats/MarketsTable/index.tsx b/apps/evm/src/pages/Stats/MarketsTable/index.tsx new file mode 100644 index 0000000000..b52e89c65a --- /dev/null +++ b/apps/evm/src/pages/Stats/MarketsTable/index.tsx @@ -0,0 +1,314 @@ +import { getBlockTimeByChainId } from '@venusprotocol/chains'; +import BigNumber from 'bignumber.js'; +import { type ApiRiskDashboardMarket, useGetRiskDashboardMarkets } from 'clients/api'; +import { Spinner, Table, type TableColumn, TokenIconWithSymbol } from 'components'; +import { PLACEHOLDER_KEY } from 'constants/placeholders'; +import { useGetVTokens } from 'libs/tokens/hooks/useGetVTokens'; +import { useTranslation } from 'libs/translations'; +import { useChainId } from 'libs/wallet'; +import { useMemo } from 'react'; +import type { Token } from 'types'; +import { + calculateDailyTokenRate, + calculateYearlyPercentageRate, + formatCentsToReadableValue, + formatPercentageToReadableValue, +} from 'utilities'; +import { getAddress } from 'viem'; + +interface MarketRow extends ApiRiskDashboardMarket { + underlyingToken: Token | null; + utilization: number | null; + supplyApyPercentage: number | null; + borrowApyPercentage: number | null; + supplyCapFilledFraction: number | null; + borrowCapFilledFraction: number | null; + topSupplierShareFraction: number | null; + topBorrowerShareFraction: number | null; +} + +const toNumber = (cents: string | null) => (cents === null ? null : Number(cents)); + +const ratio = (numerator: string | null, denominator: string) => { + if (numerator === null) { + return null; + } + const denominatorBn = new BigNumber(denominator); + if (denominatorBn.isZero()) { + return null; + } + return new BigNumber(numerator).dividedBy(denominatorBn).toNumber(); +}; + +const calculateApyPercentage = (ratePerBlockMantissa: string, blocksPerDay?: number) => { + const rateMantissa = new BigNumber(ratePerBlockMantissa); + if (rateMantissa.isZero()) { + return 0; + } + const dailyPercentageRate = calculateDailyTokenRate({ + rateMantissa, + blocksPerDay, + }); + return calculateYearlyPercentageRate({ dailyPercentageRate }).toNumber(); +}; + +const renderUsdCell = (cents: string | null) => { + if (cents === null) { + return PLACEHOLDER_KEY; + } + return formatCentsToReadableValue({ value: cents }); +}; + +const renderPercentageCell = (fraction: number | null) => { + if (fraction === null) { + return PLACEHOLDER_KEY; + } + return formatPercentageToReadableValue(fraction * 100); +}; + +const compareNullable = (a: number | null, b: number | null, direction: 'asc' | 'desc') => { + if (a === null && b === null) return 0; + if (a === null) return 1; + if (b === null) return -1; + return direction === 'asc' ? a - b : b - a; +}; + +export const MarketsTable: React.FC = () => { + const { t } = useTranslation(); + const { chainId } = useChainId(); + const vTokens = useGetVTokens({ chainId }); + const { data, isLoading, isError } = useGetRiskDashboardMarkets(); + + const blocksPerDay = getBlockTimeByChainId({ chainId })?.blocksPerDay; + + const underlyingByVToken = useMemo(() => { + const map = new Map(); + for (const vToken of vTokens) { + map.set(getAddress(vToken.address), vToken.underlyingToken); + } + return map; + }, [vTokens]); + + const rows = useMemo(() => { + if (!data) { + return []; + } + return data.markets.map(market => ({ + ...market, + underlyingToken: underlyingByVToken.get(market.marketAddress) ?? null, + utilization: ratio(market.totalBorrowsUsdCents, market.totalSupplyUsdCents), + supplyApyPercentage: calculateApyPercentage(market.supplyRatePerBlockMantissa, blocksPerDay), + borrowApyPercentage: calculateApyPercentage(market.borrowRatePerBlockMantissa, blocksPerDay), + supplyCapFilledFraction: ratio(market.totalSupplyUsdCents, market.supplyCapUsdCents ?? '0'), + borrowCapFilledFraction: ratio(market.totalBorrowsUsdCents, market.borrowCapUsdCents ?? '0'), + topSupplierShareFraction: ratio(market.topSupplierUsdCents, market.totalSupplyUsdCents), + topBorrowerShareFraction: ratio(market.topBorrowerUsdCents, market.totalBorrowsUsdCents), + })); + }, [data, underlyingByVToken, blocksPerDay]); + + const columns = useMemo[]>( + () => [ + { + key: 'asset', + label: t('statsPage.marketsTable.columns.asset'), + selectOptionLabel: t('statsPage.marketsTable.columns.asset'), + align: 'left', + renderCell: row => + row.underlyingToken ? ( + + ) : ( + row.marketAddress + ), + sortRows: (a, b, direction) => { + const symbolA = a.underlyingToken?.symbol ?? a.marketAddress; + const symbolB = b.underlyingToken?.symbol ?? b.marketAddress; + return direction === 'asc' + ? symbolA.localeCompare(symbolB) + : symbolB.localeCompare(symbolA); + }, + }, + { + key: 'totalSupplyUsdCents', + label: t('statsPage.marketsTable.columns.totalSupply'), + selectOptionLabel: t('statsPage.marketsTable.columns.totalSupply'), + align: 'right', + renderCell: row => renderUsdCell(row.totalSupplyUsdCents), + sortRows: (a, b, direction) => + compareNullable( + toNumber(a.totalSupplyUsdCents), + toNumber(b.totalSupplyUsdCents), + direction, + ), + }, + { + key: 'totalBorrowsUsdCents', + label: t('statsPage.marketsTable.columns.totalBorrows'), + selectOptionLabel: t('statsPage.marketsTable.columns.totalBorrows'), + align: 'right', + renderCell: row => renderUsdCell(row.totalBorrowsUsdCents), + sortRows: (a, b, direction) => + compareNullable( + toNumber(a.totalBorrowsUsdCents), + toNumber(b.totalBorrowsUsdCents), + direction, + ), + }, + { + key: 'utilization', + label: t('statsPage.marketsTable.columns.utilization'), + selectOptionLabel: t('statsPage.marketsTable.columns.utilization'), + align: 'right', + renderCell: row => renderPercentageCell(row.utilization), + sortRows: (a, b, direction) => compareNullable(a.utilization, b.utilization, direction), + }, + { + key: 'supplyApy', + label: t('statsPage.marketsTable.columns.supplyApy'), + selectOptionLabel: t('statsPage.marketsTable.columns.supplyApy'), + align: 'right', + renderCell: row => + row.supplyApyPercentage === null + ? PLACEHOLDER_KEY + : formatPercentageToReadableValue(row.supplyApyPercentage), + sortRows: (a, b, direction) => + compareNullable(a.supplyApyPercentage, b.supplyApyPercentage, direction), + }, + { + key: 'borrowApy', + label: t('statsPage.marketsTable.columns.borrowApy'), + selectOptionLabel: t('statsPage.marketsTable.columns.borrowApy'), + align: 'right', + renderCell: row => + row.borrowApyPercentage === null + ? PLACEHOLDER_KEY + : formatPercentageToReadableValue(row.borrowApyPercentage), + sortRows: (a, b, direction) => + compareNullable(a.borrowApyPercentage, b.borrowApyPercentage, direction), + }, + { + key: 'totalDebtAgainst', + label: t('statsPage.marketsTable.columns.totalDebtAgainst'), + selectOptionLabel: t('statsPage.marketsTable.columns.totalDebtAgainst'), + align: 'right', + renderCell: row => renderUsdCell(row.totalDebtAgainstUsdCents), + }, + { + key: 'stablecoinDebt', + label: t('statsPage.marketsTable.columns.stablecoinDebt'), + selectOptionLabel: t('statsPage.marketsTable.columns.stablecoinDebt'), + align: 'right', + renderCell: row => renderUsdCell(row.stablecoinDebtUsdCents), + }, + { + key: 'bnbDebt', + label: t('statsPage.marketsTable.columns.bnbDebt'), + selectOptionLabel: t('statsPage.marketsTable.columns.bnbDebt'), + align: 'right', + renderCell: row => renderUsdCell(row.bnbDebtUsdCents), + }, + { + key: 'otherDebt', + label: t('statsPage.marketsTable.columns.otherDebt'), + selectOptionLabel: t('statsPage.marketsTable.columns.otherDebt'), + align: 'right', + renderCell: row => renderUsdCell(row.otherDebtUsdCents), + }, + { + key: 'liquidityUsdCents', + label: t('statsPage.marketsTable.columns.liquidity'), + selectOptionLabel: t('statsPage.marketsTable.columns.liquidity'), + align: 'right', + renderCell: row => renderUsdCell(row.liquidityUsdCents), + sortRows: (a, b, direction) => + compareNullable(toNumber(a.liquidityUsdCents), toNumber(b.liquidityUsdCents), direction), + }, + { + key: 'supplyCap', + label: t('statsPage.marketsTable.columns.supplyCap'), + selectOptionLabel: t('statsPage.marketsTable.columns.supplyCap'), + align: 'right', + renderCell: row => renderUsdCell(row.supplyCapUsdCents), + sortRows: (a, b, direction) => + compareNullable(toNumber(a.supplyCapUsdCents), toNumber(b.supplyCapUsdCents), direction), + }, + { + key: 'borrowCap', + label: t('statsPage.marketsTable.columns.borrowCap'), + selectOptionLabel: t('statsPage.marketsTable.columns.borrowCap'), + align: 'right', + renderCell: row => renderUsdCell(row.borrowCapUsdCents), + sortRows: (a, b, direction) => + compareNullable(toNumber(a.borrowCapUsdCents), toNumber(b.borrowCapUsdCents), direction), + }, + { + key: 'supplyCapFilled', + label: t('statsPage.marketsTable.columns.supplyCapFilled'), + selectOptionLabel: t('statsPage.marketsTable.columns.supplyCapFilled'), + align: 'right', + renderCell: row => renderPercentageCell(row.supplyCapFilledFraction), + sortRows: (a, b, direction) => + compareNullable(a.supplyCapFilledFraction, b.supplyCapFilledFraction, direction), + }, + { + key: 'borrowCapFilled', + label: t('statsPage.marketsTable.columns.borrowCapFilled'), + selectOptionLabel: t('statsPage.marketsTable.columns.borrowCapFilled'), + align: 'right', + renderCell: row => renderPercentageCell(row.borrowCapFilledFraction), + sortRows: (a, b, direction) => + compareNullable(a.borrowCapFilledFraction, b.borrowCapFilledFraction, direction), + }, + { + key: 'topSupplierShare', + label: t('statsPage.marketsTable.columns.topSupplierShare'), + selectOptionLabel: t('statsPage.marketsTable.columns.topSupplierShare'), + align: 'right', + renderCell: row => renderPercentageCell(row.topSupplierShareFraction), + sortRows: (a, b, direction) => + compareNullable(a.topSupplierShareFraction, b.topSupplierShareFraction, direction), + }, + { + key: 'topBorrowerShare', + label: t('statsPage.marketsTable.columns.topBorrowerShare'), + selectOptionLabel: t('statsPage.marketsTable.columns.topBorrowerShare'), + align: 'right', + renderCell: row => renderPercentageCell(row.topBorrowerShareFraction), + sortRows: (a, b, direction) => + compareNullable(a.topBorrowerShareFraction, b.topBorrowerShareFraction, direction), + }, + ], + [t], + ); + + if (isLoading) { + return ( +
+ +
+ ); + } + + if (isError || !data) { + return

{t('statsPage.marketsTable.unavailable')}

; + } + + if (rows.length === 0) { + return

{t('statsPage.marketsTable.noData')}

; + } + + const totalSupplyColumn = columns.find(col => col.key === 'totalSupplyUsdCents'); + + return ( +
row.marketAddress} + minWidth="1600px" + tableLayout="auto" + initialOrder={ + totalSupplyColumn ? { orderBy: totalSupplyColumn, orderDirection: 'desc' } : undefined + } + /> + ); +}; From 1db31d8f6ef85201cb6ad5e16f23cc88d7bfa96b Mon Sep 17 00:00:00 2001 From: Gleiser Oliveira Date: Wed, 27 May 2026 20:36:37 -0600 Subject: [PATCH 7/8] feat: add RiskParametersTable --- .../pages/Stats/RiskParametersTable/index.tsx | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 apps/evm/src/pages/Stats/RiskParametersTable/index.tsx diff --git a/apps/evm/src/pages/Stats/RiskParametersTable/index.tsx b/apps/evm/src/pages/Stats/RiskParametersTable/index.tsx new file mode 100644 index 0000000000..4578753d1b --- /dev/null +++ b/apps/evm/src/pages/Stats/RiskParametersTable/index.tsx @@ -0,0 +1,195 @@ +import { type ApiRiskDashboardMarket, useGetRiskDashboardMarkets } from 'clients/api'; +import { Spinner, Table, type TableColumn, TokenIconWithSymbol } from 'components'; +import { PLACEHOLDER_KEY } from 'constants/placeholders'; +import { useGetVTokens } from 'libs/tokens/hooks/useGetVTokens'; +import { useTranslation } from 'libs/translations'; +import { useMemo } from 'react'; +import type { Token } from 'types'; +import { + calculatePercentage, + convertPercentageFromSmartContract, + convertPriceMantissaToDollars, + formatCentsToReadableValue, + formatPercentageToReadableValue, +} from 'utilities'; +import { getAddress } from 'viem'; + +interface RiskRow extends ApiRiskDashboardMarket { + underlyingToken: Token | null; + priceCents: number | null; + utilization: number; +} + +const sortByMantissa = (a: string, b: string, direction: 'asc' | 'desc') => { + const aValue = convertPercentageFromSmartContract(a); + const bValue = convertPercentageFromSmartContract(b); + return direction === 'asc' ? aValue - bValue : bValue - aValue; +}; + +export const RiskParametersTable: React.FC = () => { + const { t } = useTranslation(); + const vTokens = useGetVTokens(); + const { data, isLoading, isError } = useGetRiskDashboardMarkets(); + + const underlyingByVToken = useMemo(() => { + const map = new Map(); + for (const vToken of vTokens) { + map.set(getAddress(vToken.address), vToken.underlyingToken); + } + return map; + }, [vTokens]); + + const rows = useMemo(() => { + if (!data) { + return []; + } + return data.markets.map(market => { + const underlyingToken = underlyingByVToken.get(market.marketAddress) ?? null; + const priceCents = underlyingToken + ? convertPriceMantissaToDollars({ + priceMantissa: market.priceMantissa, + decimals: underlyingToken.decimals, + }) + .multipliedBy(100) + .toNumber() + : null; + const utilization = calculatePercentage({ + numerator: Number(market.totalBorrowsUsdCents), + denominator: Number(market.totalSupplyUsdCents), + }); + return { ...market, underlyingToken, priceCents, utilization }; + }); + }, [data, underlyingByVToken]); + + const columns = useMemo[]>( + () => [ + { + key: 'asset', + label: t('statsPage.riskParametersTable.columns.asset'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.asset'), + align: 'left', + renderCell: row => + row.underlyingToken ? ( + + ) : ( + row.marketAddress + ), + }, + { + key: 'price', + label: t('statsPage.riskParametersTable.columns.price'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.price'), + align: 'right', + renderCell: row => + row.priceCents === null + ? PLACEHOLDER_KEY + : formatCentsToReadableValue({ value: row.priceCents }), + }, + { + key: 'collateralFactor', + label: t('statsPage.riskParametersTable.columns.collateralFactor'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.collateralFactor'), + align: 'right', + renderCell: row => + formatPercentageToReadableValue( + convertPercentageFromSmartContract(row.collateralFactorMantissa), + ), + sortRows: (a, b, direction) => + sortByMantissa(a.collateralFactorMantissa, b.collateralFactorMantissa, direction), + }, + { + key: 'liquidationThreshold', + label: t('statsPage.riskParametersTable.columns.liquidationThreshold'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.liquidationThreshold'), + align: 'right', + renderCell: row => + formatPercentageToReadableValue( + convertPercentageFromSmartContract(row.liquidationThresholdMantissa), + ), + sortRows: (a, b, direction) => + sortByMantissa(a.liquidationThresholdMantissa, b.liquidationThresholdMantissa, direction), + }, + { + key: 'liquidationIncentive', + label: t('statsPage.riskParametersTable.columns.liquidationIncentive'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.liquidationIncentive'), + align: 'right', + renderCell: row => + formatPercentageToReadableValue( + convertPercentageFromSmartContract(row.liquidationIncentiveMantissa), + ), + sortRows: (a, b, direction) => + sortByMantissa(a.liquidationIncentiveMantissa, b.liquidationIncentiveMantissa, direction), + }, + { + key: 'reserveFactor', + label: t('statsPage.riskParametersTable.columns.reserveFactor'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.reserveFactor'), + align: 'right', + renderCell: row => + formatPercentageToReadableValue( + convertPercentageFromSmartContract(row.reserveFactorMantissa), + ), + sortRows: (a, b, direction) => + sortByMantissa(a.reserveFactorMantissa, b.reserveFactorMantissa, direction), + }, + { + key: 'supplyCap', + label: t('statsPage.riskParametersTable.columns.supplyCap'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.supplyCap'), + align: 'right', + renderCell: row => + row.supplyCapUsdCents === null + ? PLACEHOLDER_KEY + : formatCentsToReadableValue({ value: row.supplyCapUsdCents }), + }, + { + key: 'borrowCap', + label: t('statsPage.riskParametersTable.columns.borrowCap'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.borrowCap'), + align: 'right', + renderCell: row => + row.borrowCapUsdCents === null + ? PLACEHOLDER_KEY + : formatCentsToReadableValue({ value: row.borrowCapUsdCents }), + }, + { + key: 'utilization', + label: t('statsPage.riskParametersTable.columns.utilization'), + selectOptionLabel: t('statsPage.riskParametersTable.columns.utilization'), + align: 'right', + renderCell: row => formatPercentageToReadableValue(row.utilization), + sortRows: (a, b, direction) => + direction === 'asc' ? a.utilization - b.utilization : b.utilization - a.utilization, + }, + ], + [t], + ); + + if (isLoading) { + return ( +
+ +
+ ); + } + + if (isError || !data) { + return

{t('statsPage.riskParametersTable.unavailable')}

; + } + + if (rows.length === 0) { + return

{t('statsPage.riskParametersTable.noData')}

; + } + + return ( +
row.marketAddress} + minWidth="1200px" + tableLayout="auto" + /> + ); +}; From 4e066dfe5db24060deacbb101d6e738eeb70c270 Mon Sep 17 00:00:00 2001 From: Gleiser Oliveira Date: Wed, 27 May 2026 20:37:13 -0600 Subject: [PATCH 8/8] chore: translations for the market tables --- .../libs/translations/translations/en.json | 56 +++++++++++++++++++ .../libs/translations/translations/ja.json | 56 +++++++++++++++++++ .../libs/translations/translations/th.json | 56 +++++++++++++++++++ .../libs/translations/translations/tr.json | 56 +++++++++++++++++++ .../libs/translations/translations/vi.json | 56 +++++++++++++++++++ .../translations/translations/zh-Hans.json | 56 +++++++++++++++++++ .../translations/translations/zh-Hant.json | 56 +++++++++++++++++++ 7 files changed, 392 insertions(+) diff --git a/apps/evm/src/libs/translations/translations/en.json b/apps/evm/src/libs/translations/translations/en.json index fc3cb0e6a1..c37ea2dd1b 100644 --- a/apps/evm/src/libs/translations/translations/en.json +++ b/apps/evm/src/libs/translations/translations/en.json @@ -1844,6 +1844,22 @@ "labelTooltip": "This is your current spending cap for this asset. To spend more, revoke the cap and set a new one." }, "statsPage": { + "eModeTable": { + "columns": { + "asset": "Asset", + "borrowable": "Borrowable", + "collateral": "Collateral", + "collateralFactor": "Collateral Factor", + "group": "E-Mode Group", + "liquidationIncentive": "Liquidation Incentive", + "liquidationThreshold": "Liquidation Threshold" + }, + "no": "No", + "noData": "No E-Mode groups configured.", + "title": "E-Mode Groups", + "unavailable": "E-Mode data unavailable.", + "yes": "Yes" + }, "historicalDominance": { "borrows": { "noData": "No historical snapshots yet.", @@ -1885,6 +1901,46 @@ "unavailable": "Market data unavailable.", "utilization": "Utilization" }, + "marketsTable": { + "columns": { + "asset": "Asset", + "bnbDebt": "BNB Debt", + "borrowApy": "Borrow APY", + "borrowCap": "Borrow Cap", + "borrowCapFilled": "Borrow Cap %", + "liquidity": "Liquidity", + "otherDebt": "Other Debt", + "stablecoinDebt": "Stablecoin Debt", + "supplyApy": "Supply APY", + "supplyCap": "Supply Cap", + "supplyCapFilled": "Supply Cap %", + "topBorrowerShare": "Top Borrower %", + "topSupplierShare": "Top Supplier %", + "totalBorrows": "Total Borrows", + "totalDebtAgainst": "Total Debt Against", + "totalSupply": "Total Supply", + "utilization": "Utilization" + }, + "noData": "No market snapshot yet.", + "title": "Markets", + "unavailable": "Markets data unavailable." + }, + "riskParametersTable": { + "columns": { + "asset": "Asset", + "borrowCap": "Borrow Cap", + "collateralFactor": "Collateral Factor", + "liquidationIncentive": "Liquidation Incentive", + "liquidationThreshold": "Liquidation Threshold", + "price": "Price", + "reserveFactor": "Reserve Factor", + "supplyCap": "Supply Cap", + "utilization": "Utilization" + }, + "noData": "No market snapshot yet.", + "title": "Risk Parameters by Market", + "unavailable": "Risk parameters unavailable." + }, "subtitle": "Real-time risk parameters for Venus Protocol markets", "tabs": { "liquidations": "Liquidations", diff --git a/apps/evm/src/libs/translations/translations/ja.json b/apps/evm/src/libs/translations/translations/ja.json index 826f7d5312..bfa454c071 100644 --- a/apps/evm/src/libs/translations/translations/ja.json +++ b/apps/evm/src/libs/translations/translations/ja.json @@ -1844,6 +1844,22 @@ "labelTooltip": "これはこの資産の現在の支出上限です。さらに支出するには上限を取り消し、新しい上限を設定してください。" }, "statsPage": { + "eModeTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowable": "TRANSLATION NEEDED", + "collateral": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "group": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED" + }, + "no": "TRANSLATION NEEDED", + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED", + "yes": "TRANSLATION NEEDED" + }, "historicalDominance": { "borrows": { "noData": "TRANSLATION NEEDED", @@ -1885,6 +1901,46 @@ "unavailable": "TRANSLATION NEEDED", "utilization": "TRANSLATION NEEDED" }, + "marketsTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "bnbDebt": "TRANSLATION NEEDED", + "borrowApy": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "borrowCapFilled": "TRANSLATION NEEDED", + "liquidity": "TRANSLATION NEEDED", + "otherDebt": "TRANSLATION NEEDED", + "stablecoinDebt": "TRANSLATION NEEDED", + "supplyApy": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "supplyCapFilled": "TRANSLATION NEEDED", + "topBorrowerShare": "TRANSLATION NEEDED", + "topSupplierShare": "TRANSLATION NEEDED", + "totalBorrows": "TRANSLATION NEEDED", + "totalDebtAgainst": "TRANSLATION NEEDED", + "totalSupply": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, + "riskParametersTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED", + "price": "TRANSLATION NEEDED", + "reserveFactor": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, "subtitle": "Venus Protocol マーケットのリアルタイムリスクパラメータ", "tabs": { "liquidations": "TRANSLATION NEEDED", diff --git a/apps/evm/src/libs/translations/translations/th.json b/apps/evm/src/libs/translations/translations/th.json index 1916d8c91e..1d7d138580 100644 --- a/apps/evm/src/libs/translations/translations/th.json +++ b/apps/evm/src/libs/translations/translations/th.json @@ -1844,6 +1844,22 @@ "labelTooltip": "นี่คือขีดจำกัดการใช้จ่ายปัจจุบันของสินทรัพย์นี้ หากต้องการใช้จ่ายมากขึ้น ให้ยกเลิกขีดจำกัดแล้วตั้งใหม่" }, "statsPage": { + "eModeTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowable": "TRANSLATION NEEDED", + "collateral": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "group": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED" + }, + "no": "TRANSLATION NEEDED", + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED", + "yes": "TRANSLATION NEEDED" + }, "historicalDominance": { "borrows": { "noData": "TRANSLATION NEEDED", @@ -1885,6 +1901,46 @@ "unavailable": "TRANSLATION NEEDED", "utilization": "TRANSLATION NEEDED" }, + "marketsTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "bnbDebt": "TRANSLATION NEEDED", + "borrowApy": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "borrowCapFilled": "TRANSLATION NEEDED", + "liquidity": "TRANSLATION NEEDED", + "otherDebt": "TRANSLATION NEEDED", + "stablecoinDebt": "TRANSLATION NEEDED", + "supplyApy": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "supplyCapFilled": "TRANSLATION NEEDED", + "topBorrowerShare": "TRANSLATION NEEDED", + "topSupplierShare": "TRANSLATION NEEDED", + "totalBorrows": "TRANSLATION NEEDED", + "totalDebtAgainst": "TRANSLATION NEEDED", + "totalSupply": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, + "riskParametersTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED", + "price": "TRANSLATION NEEDED", + "reserveFactor": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, "subtitle": "พารามิเตอร์ความเสี่ยงแบบเรียลไทม์สำหรับตลาด Venus Protocol", "tabs": { "liquidations": "TRANSLATION NEEDED", diff --git a/apps/evm/src/libs/translations/translations/tr.json b/apps/evm/src/libs/translations/translations/tr.json index 7539b9f55a..5dc3dc6ca6 100644 --- a/apps/evm/src/libs/translations/translations/tr.json +++ b/apps/evm/src/libs/translations/translations/tr.json @@ -1844,6 +1844,22 @@ "labelTooltip": "Bu, bu varlık için mevcut harcama limitinizdir. Daha fazla harcamak için limiti iptal edip yeni bir limit belirleyin." }, "statsPage": { + "eModeTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowable": "TRANSLATION NEEDED", + "collateral": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "group": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED" + }, + "no": "TRANSLATION NEEDED", + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED", + "yes": "TRANSLATION NEEDED" + }, "historicalDominance": { "borrows": { "noData": "TRANSLATION NEEDED", @@ -1885,6 +1901,46 @@ "unavailable": "TRANSLATION NEEDED", "utilization": "TRANSLATION NEEDED" }, + "marketsTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "bnbDebt": "TRANSLATION NEEDED", + "borrowApy": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "borrowCapFilled": "TRANSLATION NEEDED", + "liquidity": "TRANSLATION NEEDED", + "otherDebt": "TRANSLATION NEEDED", + "stablecoinDebt": "TRANSLATION NEEDED", + "supplyApy": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "supplyCapFilled": "TRANSLATION NEEDED", + "topBorrowerShare": "TRANSLATION NEEDED", + "topSupplierShare": "TRANSLATION NEEDED", + "totalBorrows": "TRANSLATION NEEDED", + "totalDebtAgainst": "TRANSLATION NEEDED", + "totalSupply": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, + "riskParametersTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED", + "price": "TRANSLATION NEEDED", + "reserveFactor": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, "subtitle": "Venus Protocol piyasaları için gerçek zamanlı risk parametreleri", "tabs": { "liquidations": "TRANSLATION NEEDED", diff --git a/apps/evm/src/libs/translations/translations/vi.json b/apps/evm/src/libs/translations/translations/vi.json index ef88534b72..f59cf5ed65 100644 --- a/apps/evm/src/libs/translations/translations/vi.json +++ b/apps/evm/src/libs/translations/translations/vi.json @@ -1844,6 +1844,22 @@ "labelTooltip": "Đây là hạn mức chi tiêu hiện tại của bạn cho tài sản này. Để chi tiêu nhiều hơn, hãy hủy hạn mức và đặt lại." }, "statsPage": { + "eModeTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowable": "TRANSLATION NEEDED", + "collateral": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "group": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED" + }, + "no": "TRANSLATION NEEDED", + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED", + "yes": "TRANSLATION NEEDED" + }, "historicalDominance": { "borrows": { "noData": "TRANSLATION NEEDED", @@ -1885,6 +1901,46 @@ "unavailable": "TRANSLATION NEEDED", "utilization": "TRANSLATION NEEDED" }, + "marketsTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "bnbDebt": "TRANSLATION NEEDED", + "borrowApy": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "borrowCapFilled": "TRANSLATION NEEDED", + "liquidity": "TRANSLATION NEEDED", + "otherDebt": "TRANSLATION NEEDED", + "stablecoinDebt": "TRANSLATION NEEDED", + "supplyApy": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "supplyCapFilled": "TRANSLATION NEEDED", + "topBorrowerShare": "TRANSLATION NEEDED", + "topSupplierShare": "TRANSLATION NEEDED", + "totalBorrows": "TRANSLATION NEEDED", + "totalDebtAgainst": "TRANSLATION NEEDED", + "totalSupply": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, + "riskParametersTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED", + "price": "TRANSLATION NEEDED", + "reserveFactor": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, "subtitle": "Thông số rủi ro thời gian thực cho thị trường Venus Protocol", "tabs": { "liquidations": "TRANSLATION NEEDED", diff --git a/apps/evm/src/libs/translations/translations/zh-Hans.json b/apps/evm/src/libs/translations/translations/zh-Hans.json index 7f0db6c50d..f21ad07892 100644 --- a/apps/evm/src/libs/translations/translations/zh-Hans.json +++ b/apps/evm/src/libs/translations/translations/zh-Hans.json @@ -1844,6 +1844,22 @@ "labelTooltip": "这是你当前对该资产的支出上限。如需更多支出,请撤销上限并重新设置。" }, "statsPage": { + "eModeTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowable": "TRANSLATION NEEDED", + "collateral": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "group": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED" + }, + "no": "TRANSLATION NEEDED", + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED", + "yes": "TRANSLATION NEEDED" + }, "historicalDominance": { "borrows": { "noData": "TRANSLATION NEEDED", @@ -1885,6 +1901,46 @@ "unavailable": "TRANSLATION NEEDED", "utilization": "TRANSLATION NEEDED" }, + "marketsTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "bnbDebt": "TRANSLATION NEEDED", + "borrowApy": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "borrowCapFilled": "TRANSLATION NEEDED", + "liquidity": "TRANSLATION NEEDED", + "otherDebt": "TRANSLATION NEEDED", + "stablecoinDebt": "TRANSLATION NEEDED", + "supplyApy": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "supplyCapFilled": "TRANSLATION NEEDED", + "topBorrowerShare": "TRANSLATION NEEDED", + "topSupplierShare": "TRANSLATION NEEDED", + "totalBorrows": "TRANSLATION NEEDED", + "totalDebtAgainst": "TRANSLATION NEEDED", + "totalSupply": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, + "riskParametersTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED", + "price": "TRANSLATION NEEDED", + "reserveFactor": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, "subtitle": "Venus Protocol 市场的实时风险参数", "tabs": { "liquidations": "TRANSLATION NEEDED", diff --git a/apps/evm/src/libs/translations/translations/zh-Hant.json b/apps/evm/src/libs/translations/translations/zh-Hant.json index 6a0a374c10..84713714b9 100644 --- a/apps/evm/src/libs/translations/translations/zh-Hant.json +++ b/apps/evm/src/libs/translations/translations/zh-Hant.json @@ -1844,6 +1844,22 @@ "labelTooltip": "這是你當前對該資產的支出上限。如需更多支出,請撤銷上限並重新設置。" }, "statsPage": { + "eModeTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowable": "TRANSLATION NEEDED", + "collateral": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "group": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED" + }, + "no": "TRANSLATION NEEDED", + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED", + "yes": "TRANSLATION NEEDED" + }, "historicalDominance": { "borrows": { "noData": "TRANSLATION NEEDED", @@ -1885,6 +1901,46 @@ "unavailable": "TRANSLATION NEEDED", "utilization": "TRANSLATION NEEDED" }, + "marketsTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "bnbDebt": "TRANSLATION NEEDED", + "borrowApy": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "borrowCapFilled": "TRANSLATION NEEDED", + "liquidity": "TRANSLATION NEEDED", + "otherDebt": "TRANSLATION NEEDED", + "stablecoinDebt": "TRANSLATION NEEDED", + "supplyApy": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "supplyCapFilled": "TRANSLATION NEEDED", + "topBorrowerShare": "TRANSLATION NEEDED", + "topSupplierShare": "TRANSLATION NEEDED", + "totalBorrows": "TRANSLATION NEEDED", + "totalDebtAgainst": "TRANSLATION NEEDED", + "totalSupply": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, + "riskParametersTable": { + "columns": { + "asset": "TRANSLATION NEEDED", + "borrowCap": "TRANSLATION NEEDED", + "collateralFactor": "TRANSLATION NEEDED", + "liquidationIncentive": "TRANSLATION NEEDED", + "liquidationThreshold": "TRANSLATION NEEDED", + "price": "TRANSLATION NEEDED", + "reserveFactor": "TRANSLATION NEEDED", + "supplyCap": "TRANSLATION NEEDED", + "utilization": "TRANSLATION NEEDED" + }, + "noData": "TRANSLATION NEEDED", + "title": "TRANSLATION NEEDED", + "unavailable": "TRANSLATION NEEDED" + }, "subtitle": "Venus Protocol 市場的即時風險參數", "tabs": { "liquidations": "TRANSLATION NEEDED",