From c204ac5ad90e4b5ad82d0ed79b631e9920e4a692 Mon Sep 17 00:00:00 2001 From: bartosz-lipinski <264380+bartosz-lipinski@users.noreply.github.com> Date: Tue, 29 Dec 2020 21:29:06 -0600 Subject: [PATCH] feat: fix borrowing power --- src/App.less | 26 ++++ src/components/ReserveStatus/index.tsx | 122 ++++++++++++++++-- .../ReserveUtilizationChart/index.tsx | 16 ++- src/components/TokenIcon/index.tsx | 11 +- src/components/UserLendingCard/index.tsx | 10 +- src/components/WaterWave/index.less | 8 +- src/components/WaterWave/index.tsx | 6 +- src/constants/labels.ts | 2 +- src/constants/style.tsx | 6 +- src/hooks/useBorrowedAmount.ts | 3 +- src/views/home/index.tsx | 37 +++--- src/views/reserve/index.tsx | 30 +++-- src/views/reserve/style.less | 25 +--- 13 files changed, 217 insertions(+), 85 deletions(-) diff --git a/src/App.less b/src/App.less index f70823a..cfcfa78 100644 --- a/src/App.less +++ b/src/App.less @@ -329,6 +329,32 @@ em { text-align: right; } +.small-statisitc { + .ant-statistic-title { + font-size: 12px; + } + + .ant-statistic-content { + max-height: 20px; + line-height: 2px;; + } + + .ant-statistic-content-value-int { + font-size: 12px; + } + + .ant-statistic-content-value-decimal { + font-size: 10px; + } +} + +.dashboard-amount-quote-stat { + font-size: 10px; + font-style: normal; + text-align: center; + font-weight: normal; +} + @media only screen and (max-width: 600px) { .exchange-card { width: 360px; diff --git a/src/components/ReserveStatus/index.tsx b/src/components/ReserveStatus/index.tsx index 5099ac1..571f3b6 100644 --- a/src/components/ReserveStatus/index.tsx +++ b/src/components/ReserveStatus/index.tsx @@ -1,10 +1,15 @@ import React from "react"; -import { LendingReserve } from "../../models/lending"; -import { Card } from "antd"; +import { calculateDepositAPY, LendingReserve } from "../../models/lending"; +import { Card, Col, Row, Statistic } from "antd"; import { PublicKey } from "@solana/web3.js"; import "./style.less"; -import { LABELS } from "../../constants"; +import { GUTTER, LABELS } from "../../constants"; import { ReserveUtilizationChart } from "./../../components/ReserveUtilizationChart"; +import { useMemo } from "react"; +import { formatNumber, fromLamports, wadToLamports } from "../../utils/utils"; +import { useMint } from "../../contexts/accounts"; +import { useMidPriceInUSD } from "../../contexts/market"; +import { TokenIcon } from "../TokenIcon"; export const ReserveStatus = (props: { className?: string; @@ -17,20 +22,113 @@ export const ReserveStatus = (props: { alignItems: "center", }; + const mintAddress = props.reserve.liquidityMint?.toBase58(); + const liquidityMint = useMint(mintAddress); + const { price } = useMidPriceInUSD(mintAddress); + const availableLiquidity = fromLamports( + props.reserve.availableLiquidity.toNumber(), + liquidityMint + ); + + const availableLiquidityInUSD = price * availableLiquidity; + + const totalBorrows = useMemo( + () => + fromLamports( + wadToLamports(props.reserve.borrowedLiquidityWad), + liquidityMint + ), + [props.reserve, liquidityMint] + ); + + const totalBorrowsInUSD = price * totalBorrows; + + const depositAPY = useMemo(() => calculateDepositAPY(props.reserve), [ + props.reserve + ]); + + const liquidationThreshold = props.reserve.config.liquidationThreshold; + const liquidationPenalty = props.reserve.config.liquidationBonus; + const maxLTV = props.reserve.config.loanToValueRatio; + return ( {LABELS.RESERVE_STATUS_TITLE}} + title={<> + + {LABELS.RESERVE_STATUS_TITLE}} bodyStyle={bodyStyle} > -
- +
+ + +
+ {node} +
${formatNumber.format(availableLiquidityInUSD)}
+
} + precision={2} /> + + +
+ {node} +
${formatNumber.format(totalBorrowsInUSD)}
+
} + precision={2} /> + +
+ + + + + + + + + + + + + + + + + + +
); diff --git a/src/components/ReserveUtilizationChart/index.tsx b/src/components/ReserveUtilizationChart/index.tsx index 1e457c8..2962fa0 100644 --- a/src/components/ReserveUtilizationChart/index.tsx +++ b/src/components/ReserveUtilizationChart/index.tsx @@ -3,9 +3,11 @@ import { LendingReserve } from "../../models/lending"; import { fromLamports, wadToLamports } from "../../utils/utils"; import { useMint } from "../../contexts/accounts"; import { WaterWave } from "./../WaterWave"; +import { Statistic } from "antd"; export const ReserveUtilizationChart = (props: { reserve: LendingReserve }) => { - const liquidityMint = useMint(props.reserve.liquidityMint); + const mintAddress = props.reserve.liquidityMint?.toBase58(); + const liquidityMint = useMint(mintAddress); const availableLiquidity = fromLamports( props.reserve.availableLiquidity.toNumber(), liquidityMint @@ -20,10 +22,20 @@ export const ReserveUtilizationChart = (props: { reserve: LendingReserve }) => { [props.reserve, liquidityMint] ); + const percent = (availableLiquidity * 100) / (availableLiquidity + totalBorrows); + return ( + } + percent={percent} /> ); }; diff --git a/src/components/TokenIcon/index.tsx b/src/components/TokenIcon/index.tsx index 7ad7507..2dfdc73 100644 --- a/src/components/TokenIcon/index.tsx +++ b/src/components/TokenIcon/index.tsx @@ -7,19 +7,22 @@ import { PublicKey } from "@solana/web3.js"; export const TokenIcon = (props: { mintAddress?: string | PublicKey; style?: React.CSSProperties; + size?: number; className?: string; }) => { const { tokenMap } = useConnectionConfig(); const icon = getTokenIcon(tokenMap, props.mintAddress); + const size = props.size || 20; + if (icon) { return ( Token icon
- {formatNumber.format(available)} {name} +
+
{formatNumber.format(borrowingPower)} {name}
+
${formatNumber.format(borrowingPowerInUSD)}
+
diff --git a/src/components/WaterWave/index.less b/src/components/WaterWave/index.less index 640a402..1a40059 100644 --- a/src/components/WaterWave/index.less +++ b/src/components/WaterWave/index.less @@ -4,13 +4,15 @@ display: inline-block; position: relative; transform-origin: left; + display: flex; + align-items: center; + justify-content: center; + .text { position: absolute; - left: 5px; - top: calc(50% - 15px); text-align: center; width: 100%; - span { + .title { color: @text-color-secondary; font-size: 14px; line-height: 22px; diff --git a/src/components/WaterWave/index.tsx b/src/components/WaterWave/index.tsx index 9adcc33..e52d9b8 100644 --- a/src/components/WaterWave/index.tsx +++ b/src/components/WaterWave/index.tsx @@ -5,7 +5,7 @@ export const WaterWave = (props: any) => { const node = useRef(); const root = useRef(); const [radio, setRadio] = useState(1); - const { percent, title, style, color } = props; + const { percent, title, style, color, showPercent } = props; const { height } = style; const resize = useCallback(() => { @@ -54,8 +54,8 @@ export const WaterWave = (props: any) => { />
- {title && {title}} -

{percent.toFixed(2)}%

+ {title} +

{showPercent && `${percent.toFixed(2)}%`}

); diff --git a/src/constants/labels.ts b/src/constants/labels.ts index 8729534..391cfdd 100644 --- a/src/constants/labels.ts +++ b/src/constants/labels.ts @@ -4,7 +4,7 @@ export const LABELS = { BORROWING_POWER_VALUE: "Borrowing Power", BORROWED_VALUE: "You borrowed", GIVE_SOL: "Give me SOL", - LIQUIDATION_INFO: "This view displays all loans that can be liquidated. A liquidation is a process where borrower collateral does not cover value of the loan. It is represented by health factor falling below 1.o. When a loan is liquidated, an liquidator can purchase collateral at a discount by repaing the portio of the loan. ", + LIQUIDATION_INFO: "This view displays all loans that can be liquidated. A liquidation is a process where borrower collateral does not cover value of the loan. It is represented by health factor falling below 1.0. When a loan is liquidated, an liquidator can purchase collateral at a discount by repaing the portio of the loan. ", FAUCET_INFO: "This faucet will help you fund your accounts outside of Solana main network.", ACCOUNT_FUNDED: "Account funded.", diff --git a/src/constants/style.tsx b/src/constants/style.tsx index 0c4e7f9..1818200 100644 --- a/src/constants/style.tsx +++ b/src/constants/style.tsx @@ -1 +1,5 @@ -export const GUTTER = [16, { xs: 8, sm: 16, md: 16, lg: 16 }] as any; \ No newline at end of file +export const GUTTER = [16, { xs: 8, sm: 16, md: 16, lg: 16 }] as any; + +export const SMALL_STATISTIC: React.CSSProperties = { + fontSize: 10, +}; \ No newline at end of file diff --git a/src/hooks/useBorrowedAmount.ts b/src/hooks/useBorrowedAmount.ts index c03844a..46f770e 100644 --- a/src/hooks/useBorrowedAmount.ts +++ b/src/hooks/useBorrowedAmount.ts @@ -55,7 +55,7 @@ export function useBorrowedAmount(address?: string | PublicKey) { borrowedInUSD: 0, colateralInUSD: 0, ltv: 0, - health: 0, + health: 0, }; let liquidationThreshold = 0; @@ -88,7 +88,6 @@ export function useBorrowedAmount(address?: string | PublicKey) { result.health = Number.isFinite(result.health) ? result.health : 0; } - setBorrowedInfo(result); })(); }, [connection, userObligationsByReserve, setBorrowedInfo]); diff --git a/src/views/home/index.tsx b/src/views/home/index.tsx index 719e63d..0377e74 100644 --- a/src/views/home/index.tsx +++ b/src/views/home/index.tsx @@ -19,7 +19,7 @@ export const HomeView = () => { const [totals, setTotals] = useState({ marketSize: 0, borrowed: 0, - lentOutPct: 0, + lentOutPct: 0, items: [], }) @@ -28,7 +28,7 @@ export const HomeView = () => { let newTotals: Totals = { marketSize: 0, borrowed: 0, - lentOutPct: 0, + lentOutPct: 0, items: [], }; @@ -44,14 +44,17 @@ export const HomeView = () => { return; } + const price = midPriceInUSD(liquidityMint?.pubkey.toBase58()); + let leaf = { key: item.pubkey.toBase58(), marketSize: fromLamports(marketCapLamports, liquidityMint?.info) * - midPriceInUSD(liquidityMint?.pubkey.toBase58()), + price, borrowed: fromLamports( wadToLamports(item.info?.borrowedLiquidityWad).toNumber(), liquidityMint.info - ), + ) * + price, name: getTokenName(tokenMap, item.info.liquidityMint.toBase58()) } @@ -59,7 +62,7 @@ export const HomeView = () => { newTotals.marketSize = newTotals.marketSize + leaf.marketSize; newTotals.borrowed = newTotals.borrowed + leaf.borrowed; - + }); newTotals.lentOutPct = newTotals.borrowed / newTotals.marketSize; @@ -82,8 +85,8 @@ export const HomeView = () => { return (
- @@ -128,16 +131,16 @@ export const HomeView = () => { -
-
{LABELS.TABLE_TITLE_ASSET}
-
{LABELS.TABLE_TITLE_MARKET_SIZE}
-
{LABELS.TABLE_TITLE_TOTAL_BORROWED}
-
{LABELS.TABLE_TITLE_DEPOSIT_APY}
-
{LABELS.TABLE_TITLE_BORROW_APY}
-
- {reserveAccounts.map((account) => ( - item.key === account.pubkey.toBase58())} /> - ))} +
+
{LABELS.TABLE_TITLE_ASSET}
+
{LABELS.TABLE_TITLE_MARKET_SIZE}
+
{LABELS.TABLE_TITLE_TOTAL_BORROWED}
+
{LABELS.TABLE_TITLE_DEPOSIT_APY}
+
{LABELS.TABLE_TITLE_BORROW_APY}
+
+ {reserveAccounts.map((account) => ( + item.key === account.pubkey.toBase58())} /> + ))}
); diff --git a/src/views/reserve/index.tsx b/src/views/reserve/index.tsx index 78806fe..69c34b0 100644 --- a/src/views/reserve/index.tsx +++ b/src/views/reserve/index.tsx @@ -5,6 +5,8 @@ import "./style.less"; import { UserLendingCard } from "./../../components/UserLendingCard"; import { ReserveStatus } from "./../../components/ReserveStatus"; +import { Col, Row } from "antd"; +import { GUTTER } from "../../constants"; export const ReserveView = () => { const { id } = useParams<{ id: string }>(); @@ -16,19 +18,21 @@ export const ReserveView = () => { } return ( -
-
- - -
+
+ + + + + + + +
); }; diff --git a/src/views/reserve/style.less b/src/views/reserve/style.less index 75c6bd8..369c099 100644 --- a/src/views/reserve/style.less +++ b/src/views/reserve/style.less @@ -4,27 +4,6 @@ flex: 1; } -.reserve-overview-item { - margin: 4px; -} - -.reserve-overview-container { - display: flex; - flex-wrap: wrap; - flex: 1; -} - -.reserve-overview-item-left { - flex: 60%; -} - -.reserve-overview-item-right { - flex: 30%; -} - -/* Responsive layout - makes a one column layout instead of a two-column layout */ -@media (max-width: 600px) { - .reserve-overview-item-right, .reserve-overview-item-left { - flex: 100%; - } +.user-lending-card { + height: 100%; } \ No newline at end of file