-
Notifications
You must be signed in to change notification settings - Fork 3
feat: Add Days to Liquidation column to borrowers table #405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
7ca5802
fix: skip failed pages instead of breaking entire market fetch
starksama dbb506d
fix: react-doctor anti-patterns
starksama 4917ce7
fix: address CodeRabbit review feedback
starksama 5a1d7a9
fix: final CodeRabbit nitpicks
starksama 41eeb6c
feat: Add Days to Liquidation column to borrowers table
starksama File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,10 +40,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); | ||
|
|
@@ -54,18 +59,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 ( | ||
| <div> | ||
|
|
@@ -116,26 +132,36 @@ 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">DAYS TO LIQ.</TableHead> | ||
| <TableHead className="text-right">% OF BORROW</TableHead> | ||
| </TableRow> | ||
| </TableHeader> | ||
| <TableBody className="table-body-compact"> | ||
| {borrowersWithLTV.length === 0 && !isLoading ? ( | ||
| {borrowersWithMetrics.length === 0 && !isLoading ? ( | ||
| <TableRow> | ||
| <TableCell | ||
| colSpan={5} | ||
| colSpan={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)}%`; | ||
|
|
||
| // Color code days to liquidation | ||
| const daysDisplay = borrower.daysToLiquidation !== null | ||
| ? borrower.daysToLiquidation < 30 | ||
| ? `${borrower.daysToLiquidation}` | ||
| : borrower.daysToLiquidation > 365 | ||
| ? '>365' | ||
| : `${borrower.daysToLiquidation}` | ||
| : '—'; | ||
|
|
||
| return ( | ||
| <TableRow key={`borrower-${borrower.userAddress}`}> | ||
| <TableCell> | ||
|
|
@@ -175,6 +201,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> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Color-coding per PR spec is not applied. The PR description defines explicit thresholds (red < 7 days, orange 7–30, green > 30) but the cell has a static 🎨 Proposed fix+ const daysColor =
+ borrower.daysToLiquidation === null
+ ? ''
+ : borrower.daysToLiquidation < 7
+ ? 'text-red-500'
+ : borrower.daysToLiquidation < 30
+ ? 'text-orange-500'
+ : 'text-green-500';
+
- <TableCell className="text-right text-sm">{daysDisplay}</TableCell>
+ <TableCell className={`text-right text-sm ${daysColor}`}>{daysDisplay}</TableCell>🤖 Prompt for AI Agents |
||
| <TableCell className="text-right text-sm">{percentDisplay}</TableCell> | ||
| </TableRow> | ||
| ); | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Borrowers already at or past the liquidation threshold show "—" instead of 0.
When
ltv >= lltv, the condition on line 70 fails entirely anddaysToLiquidationstaysnull, producing a "—" in the table — the same display as a borrower with no position. These are actually the most at-risk borrowers.🐛 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) { + // Already at or past liquidation threshold + daysToLiquidation = 0; }📝 Committable suggestion
🤖 Prompt for AI Agents