Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions src/features/market-detail/components/borrowers-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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));
}
Comment on lines +73 to +80
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Already-at/past-threshold positions show '—' instead of 0.

When lltv <= ltv, the guard on line 74 prevents calculation and leaves daysToLiquidation = null, so the cell renders '—' — the same glyph used for "no position at all". A row showing LTV=82% alongside '—' reads as safe rather than already-liquidatable.

Consider returning 0 for this case, which keeps the display logic and meaning consistent:

🐛 Proposed fix
  let daysToLiquidation: number | null = null;
  if (ltv > 0 && borrowApy > 0 && lltv > ltv) {
    const continuousRate = Math.log(1 + borrowApy);
    const yearsToLiquidation = Math.log(lltv / ltv) / continuousRate;
    daysToLiquidation = Math.max(0, Math.round(yearsToLiquidation * 365));
+ } else if (ltv > 0 && lltv <= ltv) {
+   daysToLiquidation = 0;
  }

Also applies to: 162-166

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/market-detail/components/borrowers-table.tsx` around lines 73 -
80, The code sets daysToLiquidation to null when ltv >= lltv (or when the guard
ltv > 0 && borrowApy > 0 && lltv > ltv fails), causing already-liquidatable
positions to render as "—"; change the logic in the daysToLiquidation
calculation (the block that computes continuousRate/yearsToLiquidation using
ltv, lltv, borrowApy) to explicitly set daysToLiquidation = 0 when lltv <= ltv
(and ltv > 0) so the UI shows 0 days for at-or-past-threshold positions; apply
the same adjustment to the other similar computation elsewhere in the file (the
second occurrence that uses the same ltv/lltv/borrowApy logic).


return {
...borrower,
ltv,
daysToLiquidation,
};
});
}, [borrowers, oraclePrice]);
}, [borrowers, oraclePrice, market.lltv, market.state.borrowApy]);

return (
<div>
Expand Down Expand Up @@ -120,27 +136,46 @@ export function BorrowersTable({ chainId, market, minShares, oraclePrice, onOpen
<TableHead className="text-right">BORROWED</TableHead>
<TableHead className="text-right">COLLATERAL</TableHead>
<TableHead className="text-right">LTV</TableHead>
<TableHead className="text-right">
<Tooltip
content={
<TooltipContent
title="Days to Liquidation"
detail="Estimated days until position reaches liquidation threshold, based on current LTV and borrow rate"
/>
}
>
<span className="cursor-help border-b border-dashed border-secondary/50">
DAYS TO LIQ.
</span>
</Tooltip>
</TableHead>
<TableHead className="text-right">% OF BORROW</TableHead>
{showDeveloperOptions && <TableHead className="text-right">ACTIONS</TableHead>}
</TableRow>
</TableHeader>
<TableBody className="table-body-compact">
{borrowersWithLTV.length === 0 && !isLoading ? (
{borrowersWithMetrics.length === 0 && !isLoading ? (
<TableRow>
<TableCell
colSpan={showDeveloperOptions ? 6 : 5}
colSpan={showDeveloperOptions ? 7 : 6}
className="text-center text-gray-400"
>
No borrowers found for this market
</TableCell>
</TableRow>
) : (
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}`
: '—';
Comment on lines +175 to +177
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing ">365" cap specified in the PR.

The PR spec says to display ">365" when the position is more than a year from liquidation. The current code renders the raw number (e.g. "400", "1200").

🐛 Proposed fix
-  const daysDisplay = borrower.daysToLiquidation !== null
-    ? `${borrower.daysToLiquidation}`
-    : '—';
+  const daysDisplay =
+    borrower.daysToLiquidation === null
+      ? '—'
+      : borrower.daysToLiquidation > 365
+      ? '>365'
+      : `${borrower.daysToLiquidation}`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const daysDisplay = borrower.daysToLiquidation !== null
? `${borrower.daysToLiquidation}`
: '—';
const daysDisplay =
borrower.daysToLiquidation === null
? '—'
: borrower.daysToLiquidation > 365
? '>365'
: `${borrower.daysToLiquidation}`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/market-detail/components/borrowers-table.tsx` around lines 175 -
177, The days display currently renders the raw borrower.daysToLiquidation
number; update the logic in borrowers-table (the daysDisplay computation) to
return '—' when borrower.daysToLiquidation is null, return '>365' when
borrower.daysToLiquidation is greater than 365, and otherwise render the numeric
string (e.g., `${borrower.daysToLiquidation}`); ensure you reference
borrower.daysToLiquidation in this conditional branch so positions >365 are
capped as specified.


return (
<TableRow key={`borrower-${borrower.userAddress}`}>
<TableCell>
Expand Down Expand Up @@ -180,6 +215,7 @@ export function BorrowersTable({ chainId, market, minShares, oraclePrice, onOpen
</div>
</TableCell>
<TableCell className="text-right text-sm">{borrower.ltv.toFixed(2)}%</TableCell>
<TableCell className="text-right text-sm">{daysDisplay}</TableCell>
<TableCell className="text-right text-sm">{percentDisplay}</TableCell>
{showDeveloperOptions && (
<TableCell className="text-right">
Expand Down