diff --git a/src/App.less b/src/App.less index 8aab355..f70823a 100644 --- a/src/App.less +++ b/src/App.less @@ -231,6 +231,10 @@ em { flex: 1; } +.card-fill { + height: 100%; +} + .card-row { box-sizing: border-box; margin: 5px 0px; @@ -295,6 +299,12 @@ em { font-weight: bold; } +.ant-select-selection-item { + .token-balance { + display: none; + }; +}; + .token-input { display: flex; align-items: center; @@ -303,6 +313,16 @@ em { margin: 5px 0px; } +.token-balance { + margin-left: auto; + margin-right: 5px; + color: @text-color-secondary; +} + +[class="ant-layout-header"] { + height: 16px !important; +} + .dashboard-amount-quote { font-size: 10px; font-style: normal; diff --git a/src/components/CollateralSelector/index.tsx b/src/components/CollateralSelector/index.tsx index 64d0f2f..ab491d5 100644 --- a/src/components/CollateralSelector/index.tsx +++ b/src/components/CollateralSelector/index.tsx @@ -1,14 +1,44 @@ import React from "react"; -import { useLendingReserves } from "../../hooks"; +import { useLendingReserves, UserDeposit, useUserDeposits } from "../../hooks"; import { LendingMarket, LendingReserve } from "../../models"; import { TokenIcon } from "../TokenIcon"; -import { getTokenName } from "../../utils/utils"; +import { formatAmount, getTokenName } from "../../utils/utils"; import { Select } from "antd"; import { useConnectionConfig } from "../../contexts/connection"; import { cache, ParsedAccount } from "../../contexts/accounts"; const { Option } = Select; +export const CollateralItem = (props: { + mint: string; + reserve: ParsedAccount; + userDeposit?: UserDeposit; + name: string; +}) => { + const { + mint, + name, + userDeposit, + } = props; + + return ( + <> +
+ + {name} + +  {" "} + {userDeposit ? formatAmount(userDeposit.info.amount): '--'} + +
+ + ); +} + export const CollateralSelector = (props: { reserve: LendingReserve; collateralReserve?: string; @@ -17,13 +47,13 @@ export const CollateralSelector = (props: { }) => { const { reserveAccounts } = useLendingReserves(); const { tokenMap } = useConnectionConfig(); + const { userDeposits } = useUserDeposits(); - const market = cache.get(props.reserve.lendingMarket) as ParsedAccount< + const market = cache.get(props.reserve?.lendingMarket) as ParsedAccount< LendingMarket >; - const onlyQuoteAllowed = !props.reserve?.liquidityMint?.equals( - market?.info?.quoteMint - ); + const onlyQuoteAllowed = props.reserve?.liquidityMint?.toBase58() !== + market?.info?.quoteMint?.toBase58(); return ( ); diff --git a/src/constants/index.tsx b/src/constants/index.tsx index 19646a6..a85c7c0 100644 --- a/src/constants/index.tsx +++ b/src/constants/index.tsx @@ -2,3 +2,4 @@ export * from "./ids"; export * from "./labels"; export * from "./math"; export * from "./marks"; +export * from "./style"; \ No newline at end of file diff --git a/src/constants/style.tsx b/src/constants/style.tsx new file mode 100644 index 0000000..0c4e7f9 --- /dev/null +++ b/src/constants/style.tsx @@ -0,0 +1 @@ +export const GUTTER = [16, { xs: 8, sm: 16, md: 16, lg: 16 }] as any; \ No newline at end of file diff --git a/src/hooks/useBorrowingPower.ts b/src/hooks/useBorrowingPower.ts index 03401a9..e1bbe89 100644 --- a/src/hooks/useBorrowingPower.ts +++ b/src/hooks/useBorrowingPower.ts @@ -2,28 +2,48 @@ import { PublicKey } from "@solana/web3.js"; import { useMemo } from "react"; import { useMidPriceInUSD } from "../contexts/market"; import { useLendingMarket } from "./useLendingMarket"; -import { useLendingReserve } from "./useLendingReserves"; +import { getLendingReserves, useLendingReserve } from "./useLendingReserves"; import { useUserDeposits } from "./useUserDeposits"; // TODO: add option to decrease buying power by overcollateralization factor export function useBorrowingPower(reserveAddress: string | PublicKey, overcollateralize = true) { const key = useMemo(() => typeof reserveAddress === 'string' ? reserveAddress : reserveAddress.toBase58(), [reserveAddress]); - const exclude = useMemo(() => new Set([key]), [key]); - const { totalInQuote } = useUserDeposits(exclude) + const reserve = useLendingReserve(key); const liquidityMint = reserve?.info.liquidityMint; - const market = useLendingMarket(liquidityMint); - const price = useMidPriceInUSD(liquidityMint.toBase58()).price; + const liquidityMintAddress = liquidityMint?.toBase58(); + const market = useLendingMarket(reserve?.info.lendingMarket); + + const quoteMintAddess = market?.info?.quoteMint?.toBase58(); + + // TODO: remove once cross-collateral is supported + const onlyQuoteAllowed = liquidityMintAddress !== + quoteMintAddess; + + const exclude = useMemo(() => new Set( + [key]), + [key]); + const inlcude = useMemo(() => { + const quoteReserve = getLendingReserves() + .find(r => r.info.liquidityMint.toBase58() === quoteMintAddess); + return onlyQuoteAllowed && quoteReserve ? + new Set([quoteReserve.pubkey.toBase58()]) : + undefined; + }, [onlyQuoteAllowed, quoteMintAddess]); + + const { totalInQuote } = useUserDeposits(exclude, inlcude) + + const price = useMidPriceInUSD(liquidityMintAddress).price; // amounts already expressed as quite mint - if(liquidityMint.toBase58() === market?.info.quoteMint?.toBase58()) { + if (liquidityMintAddress === market?.info.quoteMint?.toBase58()) { return { borrowingPower: totalInQuote, totalInQuote, }; - } + } return { borrowingPower: totalInQuote / price, diff --git a/src/hooks/useCollateralBalance.ts b/src/hooks/useCollateralBalance.ts index 74c4e9f..a3d5cb7 100644 --- a/src/hooks/useCollateralBalance.ts +++ b/src/hooks/useCollateralBalance.ts @@ -50,6 +50,7 @@ export function useUserCollateralBalance( balanceInUSD, mint: reserve?.collateralMint, accounts, + hasBalance: accounts.length > 0 && balance > 0, }; } export function calculateCollateralBalance( diff --git a/src/hooks/useLendingReserves.ts b/src/hooks/useLendingReserves.ts index 677d948..897b63d 100644 --- a/src/hooks/useLendingReserves.ts +++ b/src/hooks/useLendingReserves.ts @@ -3,11 +3,11 @@ import { useEffect, useMemo, useState } from "react"; import { LendingReserve, LendingReserveParser } from "../models/lending"; import { cache, ParsedAccount } from "./../contexts/accounts"; -const getLendingReserves = () => { +export const getLendingReserves = () => { return cache .byParser(LendingReserveParser) .map((id) => cache.get(id)) - .filter((acc) => acc !== undefined) as any[]; + .filter((acc) => acc !== undefined) as ParsedAccount[]; }; export function useLendingReserves() { diff --git a/src/hooks/useUserBalance.ts b/src/hooks/useUserBalance.ts index 3a349b8..49dd6bc 100644 --- a/src/hooks/useUserBalance.ts +++ b/src/hooks/useUserBalance.ts @@ -5,7 +5,8 @@ import { useMarkets } from "../contexts/market"; import { fromLamports } from "../utils/utils"; import { useUserAccounts } from "./useUserAccounts"; -export function useUserBalance(mint?: PublicKey, account?: PublicKey) { +export function useUserBalance(mintAddress?: PublicKey | string, account?: PublicKey) { + const mint = useMemo(() => typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58(), [mintAddress]); const { userAccounts } = useUserAccounts(); const [balanceInUSD, setBalanceInUSD] = useState(0); const { marketEmitter, midPriceInUSD } = useMarkets(); @@ -15,7 +16,7 @@ export function useUserBalance(mint?: PublicKey, account?: PublicKey) { return userAccounts .filter( (acc) => - mint?.equals(acc.info.mint) && + mint === acc.info.mint.toBase58() && (!account || account.equals(acc.pubkey)) ) .sort((a, b) => b.info.amount.sub(a.info.amount).toNumber()); @@ -32,7 +33,7 @@ export function useUserBalance(mint?: PublicKey, account?: PublicKey) { useEffect(() => { const updateBalance = () => { - setBalanceInUSD(balance * midPriceInUSD(mint?.toBase58() || '')); + setBalanceInUSD(balance * midPriceInUSD(mint || '')); } const dispose = marketEmitter.onMarket((args) => { @@ -51,5 +52,6 @@ export function useUserBalance(mint?: PublicKey, account?: PublicKey) { balanceLamports, balanceInUSD, accounts, + hasBalance: accounts.length > 0 && balance > 0, }; } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 92f2b49..7ab6274 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -148,7 +148,7 @@ const abbreviateNumber = (number: number, precision: number) => { return scaled.toFixed(precision) + suffix; }; -const format = (val: number, precision: number, abbr: boolean) => +export const formatAmount = (val: number, precision: number = 6, abbr: boolean = true) => abbr ? abbreviateNumber(val, precision) : val.toFixed(precision); export function formatTokenAmount( @@ -164,7 +164,7 @@ export function formatTokenAmount( return ""; } - return `${[prefix]}${format( + return `${[prefix]}${formatAmount( fromLamports(account, mint, rate), precision, abbr diff --git a/src/views/borrowReserve/index.tsx b/src/views/borrowReserve/index.tsx index 14efa9c..5f8ad15 100644 --- a/src/views/borrowReserve/index.tsx +++ b/src/views/borrowReserve/index.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useLendingReserve } from "../../hooks"; +import { useBorrowingPower, useLendingReserve, useUserObligations } from "../../hooks"; import { useParams } from "react-router-dom"; import "./style.less"; @@ -8,28 +8,85 @@ import { SideReserveOverview, SideReserveOverviewMode, } from "../../components/SideReserveOverview"; +import { Card, Col, Row, Statistic } from "antd"; +import { BarChartStatistic } from "../../components/BarChartStatistic"; +import { GUTTER } from "../../constants"; export const BorrowReserveView = () => { const { id } = useParams<{ id: string }>(); const lendingReserve = useLendingReserve(id); + const { userObligations, totalInQuote: loansValue } = useUserObligations(); + + const { totalInQuote: borrowingPower } = useBorrowingPower(id) if (!lendingReserve) { return null; } + const numberOfLoans = userObligations + .filter(ob => + // ob.obligation.info.borrowReserve.toBase58() === id && + ob.obligation.info.collateralInQuote > 0) + .length; + return (
-
- - -
+ + + + + + + + + + + + + + + + + + + item.obligation.info.borrowedInQuote / loansValue} + name={(item) => item.obligation.info.repayName} /> + + + + + + + + + + +
); }; diff --git a/src/views/borrowReserve/style.less b/src/views/borrowReserve/style.less index 92c08bc..9ac1cb2 100644 --- a/src/views/borrowReserve/style.less +++ b/src/views/borrowReserve/style.less @@ -2,10 +2,11 @@ display: flex; flex-direction: column; flex: 1; + overflow-x: hidden; } .borrow-reserve-item { - margin: 4px; + height: 100%; } .borrow-reserve-container { @@ -13,18 +14,3 @@ flex-wrap: wrap; flex: 1; } - -.borrow-reserve-item-left { - flex: 60%; -} - -.borrow-reserve-item-right { - flex: 30%; -} - -/* Responsive layout - makes a one column layout instead of a two-column layout */ -@media (max-width: 600px) { - .borrow-reserve-item-right, .borrow-reserve-item-left { - flex: 100%; - } -} \ No newline at end of file diff --git a/src/views/dashboard/index.tsx b/src/views/dashboard/index.tsx index e85d01f..d37e98e 100644 --- a/src/views/dashboard/index.tsx +++ b/src/views/dashboard/index.tsx @@ -1,6 +1,6 @@ import { Col, Row } from "antd"; import React from "react"; -import { LABELS } from "../../constants"; +import { GUTTER, LABELS } from "../../constants"; import { useWallet } from "../../contexts/wallet"; import { useUserDeposits, useUserObligations } from "./../../hooks"; import { DashboardObligations } from "./obligation"; @@ -28,7 +28,7 @@ export const DashboardView = () => { {LABELS.NO_LOANS_NO_DEPOSITS} )} - {connected && + {connected && {userDeposits.length >0 && ( diff --git a/src/views/home/index.tsx b/src/views/home/index.tsx index 781c72d..719e63d 100644 --- a/src/views/home/index.tsx +++ b/src/views/home/index.tsx @@ -1,7 +1,7 @@ import { MintInfo } from "@solana/spl-token"; import { Card, Col, Row, Statistic } from "antd"; import React, { useEffect, useState } from "react"; -import { LABELS } from "../../constants"; +import { GUTTER, LABELS } from "../../constants"; import { cache, ParsedAccount } from "../../contexts/accounts"; import { useConnectionConfig } from "../../contexts/connection"; import { useMarkets } from "../../contexts/market"; @@ -83,7 +83,7 @@ export const HomeView = () => { return (
diff --git a/src/views/liquidate/index.tsx b/src/views/liquidate/index.tsx index f579204..9a7a840 100644 --- a/src/views/liquidate/index.tsx +++ b/src/views/liquidate/index.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from "react"; -import { LABELS } from "../../constants"; +import { GUTTER, LABELS } from "../../constants"; import { LiquidateItem } from "./item"; import { useEnrichedLendingObligations } from "./../../hooks"; import "./style.less"; @@ -30,17 +30,17 @@ export const LiquidateView = () => {
{LABELS.LIQUIDATE_NO_LOANS}
) : (
- - - - {LABELS.LIQUIDATION_INFO} - - + + + + + {LABELS.LIQUIDATION_INFO} + + + + gutter={GUTTER}> { - -
-
{LABELS.TABLE_TITLE_ASSET}
-
{LABELS.TABLE_TITLE_LOAN_BALANCE}
-
{LABELS.TABLE_TITLE_COLLATERAL_BALANCE}
-
{LABELS.TABLE_TITLE_APY}
-
{LABELS.TABLE_TITLE_LTV}
-
{LABELS.TABLE_TITLE_HEALTH}
-
{LABELS.TABLE_TITLE_ACTION}
-
- {atRisk.map((item) => ( - - ))} -
+ + + +
+
{LABELS.TABLE_TITLE_ASSET}
+
{LABELS.TABLE_TITLE_LOAN_BALANCE}
+
{LABELS.TABLE_TITLE_COLLATERAL_BALANCE}
+
{LABELS.TABLE_TITLE_APY}
+
{LABELS.TABLE_TITLE_LTV}
+
{LABELS.TABLE_TITLE_HEALTH}
+
{LABELS.TABLE_TITLE_ACTION}
+
+ {atRisk.map((item) => ( + + ))} +
+ +
)}
diff --git a/src/views/liquidate/style.less b/src/views/liquidate/style.less index 0cd4465..b4d6cbe 100644 --- a/src/views/liquidate/style.less +++ b/src/views/liquidate/style.less @@ -44,4 +44,5 @@ flex-direction: row; flex-wrap: wrap; flex: 1; + overflow-x: hidden; } \ No newline at end of file