diff --git a/src/features/market-detail/components/borrowers-table.tsx b/src/features/market-detail/components/borrowers-table.tsx index 1d5e306f..8feaa1d2 100644 --- a/src/features/market-detail/components/borrowers-table.tsx +++ b/src/features/market-detail/components/borrowers-table.tsx @@ -44,10 +44,15 @@ export function BorrowersTable({ chainId, market, minShares, oraclePrice, onOpen const hasActiveFilter = minShares !== '0'; const tableKey = `borrowers-table-${currentPage}`; - // Calculate LTV for each borrower + // Calculate LTV and Days to Liquidation for each borrower // LTV = borrowAssets / (collateral * oraclePrice) - const borrowersWithLTV = useMemo(() => { - if (!oraclePrice || oraclePrice === 0n) return []; + // Days to Liquidation = ln(lltv/ltv) / ln(1 + borrowApy) * 365 + // (using continuous compounding: r = ln(1 + APY) to convert annual APY to continuous rate) + const borrowersWithMetrics = useMemo(() => { + if (!oraclePrice) return []; + + const lltv = Number(market.lltv) / 1e16; // lltv in WAD format (e.g., 8e17 = 80%) + const borrowApy = market.state.borrowApy; return borrowers.map((borrower) => { const borrowAssets = BigInt(borrower.borrowAssets); @@ -58,18 +63,29 @@ export function BorrowersTable({ chainId, market, minShares, oraclePrice, onOpen const collateralValueInLoan = (collateral * oraclePrice) / BigInt(10 ** 36); // Calculate LTV as a percentage - // LTV = (borrowAssets / collateralValue) * 100 let ltv = 0; if (collateralValueInLoan > 0n) { ltv = Number((borrowAssets * 10000n) / collateralValueInLoan) / 100; } + // Calculate Days to Liquidation + // Only calculate if borrower has position, LTV > 0, and borrow rate > 0 + let daysToLiquidation: number | null = null; + if (ltv > 0 && borrowApy > 0 && lltv > ltv) { + // Use continuous compounding: LTV(t) = LTV * e^(r * t) where r = ln(1 + APY) + // Solve for t when LTV(t) = lltv: t = ln(lltv/ltv) / r + const continuousRate = Math.log(1 + borrowApy); + const yearsToLiquidation = Math.log(lltv / ltv) / continuousRate; + daysToLiquidation = Math.max(0, Math.round(yearsToLiquidation * 365)); + } + return { ...borrower, ltv, + daysToLiquidation, }; }); - }, [borrowers, oraclePrice]); + }, [borrowers, oraclePrice, market.lltv, market.state.borrowApy]); return (
@@ -120,27 +136,46 @@ export function BorrowersTable({ chainId, market, minShares, oraclePrice, onOpen BORROWED COLLATERAL LTV + + + } + > + + DAYS TO LIQ. + + + % OF BORROW {showDeveloperOptions && ACTIONS} - {borrowersWithLTV.length === 0 && !isLoading ? ( + {borrowersWithMetrics.length === 0 && !isLoading ? ( No borrowers found for this market ) : ( - borrowersWithLTV.map((borrower) => { + borrowersWithMetrics.map((borrower) => { const totalBorrow = BigInt(market.state.borrowAssets); const borrowerAssets = BigInt(borrower.borrowAssets); const percentOfBorrow = totalBorrow > 0n ? (Number(borrowerAssets) / Number(totalBorrow)) * 100 : 0; const percentDisplay = percentOfBorrow < 0.01 && percentOfBorrow > 0 ? '<0.01%' : `${percentOfBorrow.toFixed(2)}%`; + // Days to liquidation display + const daysDisplay = borrower.daysToLiquidation !== null + ? `${borrower.daysToLiquidation}` + : '—'; + return ( @@ -180,6 +215,7 @@ export function BorrowersTable({ chainId, market, minShares, oraclePrice, onOpen
{borrower.ltv.toFixed(2)}% + {daysDisplay} {percentDisplay} {showDeveloperOptions && (