diff --git a/README.md b/README.md index c82b9c5..c05edcb 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,5 @@ Any content produced by Solana, or developer resources that Solana provides, are - [] Add market size on front page - [] Add github link - [] Repay from reserve (add selection for obligation/loan) -- [] \ No newline at end of file +- [] Add support for token names in URL in addition to reserve address + diff --git a/src/actions/repay.tsx b/src/actions/repay.tsx index 703ddc1..ff4d4b8 100644 --- a/src/actions/repay.tsx +++ b/src/actions/repay.tsx @@ -76,8 +76,8 @@ export const repay = async ( signers ); - const loanRatio = amountLamports / wadToLamports(obligation.info.borrowAmountWad) - .toNumber(); + const loanRatio = + amountLamports / wadToLamports(obligation.info.borrowAmountWad).toNumber(); console.log(loanRatio); // create approval for transfer transactions diff --git a/src/components/BorrowInput/index.tsx b/src/components/BorrowInput/index.tsx index a4cfc70..078e445 100644 --- a/src/components/BorrowInput/index.tsx +++ b/src/components/BorrowInput/index.tsx @@ -84,9 +84,7 @@ export const BorrowInput = (props: { justifyContent: "space-around", }} > -
- {LABELS.BORROW_QUESTION} -
+
{LABELS.BORROW_QUESTION}
) => { +export const ConnectButton = ( + props: ButtonProps & React.RefAttributes +) => { const { wallet, connected } = useWallet(); const { onClick, children, ...rest } = props; - return -} \ No newline at end of file + return ( + + ); +}; diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 7633fc4..26348b5 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -36,9 +36,7 @@ export const AppLayout = (props: any) => { return (
-
- {LABELS.AUDIT_WARNING} -
+
{LABELS.AUDIT_WARNING}
toLamports(parseFloat(value), repayLiquidityMint), [value, repayLiquidityMint]); + const lamports = useMemo( + () => toLamports(parseFloat(value), repayLiquidityMint), + [value, repayLiquidityMint] + ); - const mark = wadToLamports(obligation?.info.borrowAmountWad).toNumber() / lamports * 100; + const mark = + (wadToLamports(obligation?.info.borrowAmountWad).toNumber() / lamports) * + 100; const onRepay = useCallback(() => { if ( @@ -103,8 +113,8 @@ export const RepayInput = (props: { }} >
- {LABELS.REPAY_QUESTION} (Currently:{" "}) - {formatNumber.format(balance)} {name}) + {LABELS.REPAY_QUESTION} (Currently: ){formatNumber.format(balance)}{" "} + {name})
@@ -124,10 +134,22 @@ export const RepayInput = (props: { />
{name}
- - setValue((fromLamports(wadToLamports(obligation?.info.borrowAmountWad).toNumber(), repayLiquidityMint) * val / 100).toFixed(2))} /> + + setValue( + ( + (fromLamports( + wadToLamports(obligation?.info.borrowAmountWad).toNumber(), + repayLiquidityMint + ) * + val) / + 100 + ).toFixed(2) + ) + } + />
{LABELS.SELECT_COLLATERAL}
{ + const chartDiv = useRef(null); + + // dispose chart + useEffect(() => { + const div = chartDiv.current; + return () => { + let instance = div && echarts.getInstanceByDom(div); + instance && instance.dispose(); + }; + }, []); + + const liquidityMint = useMint(props.reserve.liquidityMint); + const avilableLiquidity = fromLamports( + props.reserve.totalLiquidity.toNumber(), + liquidityMint + ); + + const totalBorrows = useMemo( + () => + fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint), + [props.reserve, liquidityMint] + ); + + useEffect(() => { + if (!chartDiv.current) { + return; + } + + let instance = echarts.getInstanceByDom(chartDiv.current); + if (!instance) { + instance = echarts.init(chartDiv.current as any); + } + + const data = [ + { + name: 'Available Liquidity', + value: avilableLiquidity, + tokens: avilableLiquidity, + }, + { + name: 'Total Borrowed', + value: totalBorrows, + tokens: totalBorrows, + }, + ]; + + instance.setOption({ + tooltip: { + trigger: "item", + formatter: function (params: any) { + var val = formatUSD.format(params.value); + var tokenAmount = formatNumber.format(params.data.tokens); + return `${params.name}: \n${val}\n(${tokenAmount})`; + }, + }, + series: [ + { + name: "Liquidity", + type: "pie", + radius: ['50%', '70%'], + top: 0, + bottom: 0, + left: 0, + right: 0, + animation: false, + label: { + fontSize: 14, + show: true, + formatter: function (params: any) { + var val = formatUSD.format(params.value); + var tokenAmount = formatNumber.format(params.data.tokens); + return `{c|${params.name}}\n{r|${tokenAmount}}\n{r|${val}}`; + }, + rich: { + c: { + color: "#999", + lineHeight: 22, + align: "center", + }, + r: { + color: "#999", + align: "right", + }, + }, + color: "rgba(255, 255, 255, 0.5)", + }, + itemStyle: { + normal: { + borderColor: "#000", + }, + }, + data, + }, + ], + }); + }, [totalBorrows, avilableLiquidity]); + + return
+} export const ReserveStatus = (props: { className?: string; @@ -29,7 +135,7 @@ export const ReserveStatus = (props: { justifyContent: "space-around", }} > - TODO: Reserve Status - add chart +
); diff --git a/src/components/UserLendingCard/index.tsx b/src/components/UserLendingCard/index.tsx index faa7546..f67b866 100644 --- a/src/components/UserLendingCard/index.tsx +++ b/src/components/UserLendingCard/index.tsx @@ -6,12 +6,10 @@ import { useBorrowedAmount, } from "./../../hooks"; import { LendingReserve } from "../../models/lending"; -import { formatNumber, wadToLamports } from "../../utils/utils"; +import { formatNumber } from "../../utils/utils"; import { Button, Card, Typography } from "antd"; import { Link } from "react-router-dom"; import { PublicKey } from "@solana/web3.js"; -import { cache, ParsedAccount } from "../../contexts/accounts"; -import { MintInfo } from "@solana/spl-token"; const { Text } = Typography; diff --git a/src/components/WithdrawInput/index.tsx b/src/components/WithdrawInput/index.tsx index c6efe8e..d5c37bc 100644 --- a/src/components/WithdrawInput/index.tsx +++ b/src/components/WithdrawInput/index.tsx @@ -76,9 +76,7 @@ export const WithdrawInput = (props: { justifyContent: "space-around", }} > -
- {LABELS.WITHDRAW_QUESTION} -
+
{LABELS.WITHDRAW_QUESTION}
item.obligation.info.tokenMint.toBase58()), - "single"); + userObligationsByReserve.map((item) => + item.obligation.info.tokenMint.toBase58() + ), + "single" + ); array.forEach((item, index) => { const address = keys[index]; cache.add(new PublicKey(address), item, MintParser); }); - setBorrowedLamports(userObligationsByReserve.reduce((result, item) => { + setBorrowedLamports( + userObligationsByReserve.reduce((result, item) => { + const borrowed = wadToLamports( + item.obligation.info.borrowAmountWad + ).toNumber(); - const borrowed = wadToLamports(item.obligation.info.borrowAmountWad).toNumber(); + const owned = item.userAccounts.reduce( + (amount, acc) => (amount += acc.info.amount.toNumber()), + 0 + ); + const obligationMint = cache.get( + item.obligation.info.tokenMint + ) as ParsedAccount; - const owned = item.userAccounts.reduce((amount, acc) => amount += acc.info.amount.toNumber(), 0); - const obligationMint = cache.get(item.obligation.info.tokenMint) as ParsedAccount; - - result += borrowed * owned / obligationMint?.info.supply.toNumber(); - return result - - }, 0)); + result += (borrowed * owned) / obligationMint?.info.supply.toNumber(); + return result; + }, 0) + ); })(); + }, [connection, userObligationsByReserve]); - - }, [userObligationsByReserve]); - - return { borrowed: fromLamports(borrowedLamports, liquidityMint), borrowedLamports }; + return { + borrowed: fromLamports(borrowedLamports, liquidityMint), + borrowedLamports, + }; } diff --git a/src/hooks/useLendingReserves.ts b/src/hooks/useLendingReserves.ts index 1c728c9..a8e6600 100644 --- a/src/hooks/useLendingReserves.ts +++ b/src/hooks/useLendingReserves.ts @@ -41,7 +41,7 @@ export function useLendingReserve(address?: string | PublicKey) { >(); useEffect(() => { - setReserveAccount(cache.get(id || '')); + setReserveAccount(cache.get(id || "")); const dispose = cache.emitter.onCache((args) => { if (args.id === id) { diff --git a/src/hooks/useUserObligationByReserve.ts b/src/hooks/useUserObligationByReserve.ts index 7d9ce4b..fe7b7cc 100644 --- a/src/hooks/useUserObligationByReserve.ts +++ b/src/hooks/useUserObligationByReserve.ts @@ -5,14 +5,12 @@ import { PublicKey } from "@solana/web3.js"; export function useUserObligationByReserve(reserve?: string | PublicKey) { const { userObligations } = useUserObligations(); - const userObligationsByReserve = useMemo( - () => { - const id = typeof reserve === 'string' ? reserve : reserve?.toBase58(); - return userObligations.filter((item) => - item.obligation.info.borrowReserve.toBase58() === id - ) - }, [reserve, userObligations] - ); + const userObligationsByReserve = useMemo(() => { + const id = typeof reserve === "string" ? reserve : reserve?.toBase58(); + return userObligations.filter( + (item) => item.obligation.info.borrowReserve.toBase58() === id + ); + }, [reserve, userObligations]); return { userObligationsByReserve, diff --git a/src/models/lending/reserve.ts b/src/models/lending/reserve.ts index 428d199..aea1e48 100644 --- a/src/models/lending/reserve.ts +++ b/src/models/lending/reserve.ts @@ -262,21 +262,26 @@ export const withdrawInstruction = ( }; export const calculateBorrowAPY = (reserve: LendingReserve) => { - const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber() - const currentUtilization = totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows) - const optimalUtilization = reserve.config.optimalUtilizationRate + const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber(); + const currentUtilization = + totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows); + const optimalUtilization = reserve.config.optimalUtilizationRate; let borrowAPY; if (currentUtilization < optimalUtilization) { const normalized_factor = currentUtilization / optimalUtilization; const optimalBorrowRate = reserve.config.optimalBorrowRate / 100; const minBorrowRate = reserve.config.minBorrowRate / 100; - borrowAPY = normalized_factor * (optimalBorrowRate - minBorrowRate) + minBorrowRate; + borrowAPY = + normalized_factor * (optimalBorrowRate - minBorrowRate) + minBorrowRate; } else { - const normalized_factor = (currentUtilization - optimalUtilization) / (100 - optimalUtilization); + const normalized_factor = + (currentUtilization - optimalUtilization) / (100 - optimalUtilization); const optimalBorrowRate = reserve.config.optimalBorrowRate / 100; const maxBorrowRate = reserve.config.maxBorrowRate / 100; - borrowAPY = normalized_factor * (maxBorrowRate - optimalBorrowRate) + optimalBorrowRate; + borrowAPY = + normalized_factor * (maxBorrowRate - optimalBorrowRate) + + optimalBorrowRate; } return borrowAPY; -} \ No newline at end of file +}; diff --git a/src/views/dashboard/index.tsx b/src/views/dashboard/index.tsx index 7e594a7..a1752a6 100644 --- a/src/views/dashboard/index.tsx +++ b/src/views/dashboard/index.tsx @@ -2,7 +2,7 @@ import React from "react"; import { LABELS } from "../../constants"; import { useUserObligations } from "./../../hooks"; import { ObligationItem } from "./obligationItem"; -import "./style.less" +import "./style.less"; export const DashboardView = () => { const { userObligations } = useUserObligations(); @@ -14,12 +14,14 @@ export const DashboardView = () => {
{LABELS.DASHBOARD_TITLE_LOANS} - {userObligations.length > 0 &&
-
{LABELS.TABLE_TITLE_ASSET}
-
{LABELS.TABLE_TITLE_LOAN_BALANCE}
-
{LABELS.TABLE_TITLE_APY}
-
{LABELS.TABLE_TITLE_ACTION}
-
} + {userObligations.length > 0 && ( +
+
{LABELS.TABLE_TITLE_ASSET}
+
{LABELS.TABLE_TITLE_LOAN_BALANCE}
+
{LABELS.TABLE_TITLE_APY}
+
{LABELS.TABLE_TITLE_ACTION}
+
+ )} {userObligations.map((item) => { return ; })} diff --git a/src/views/dashboard/obligationItem.tsx b/src/views/dashboard/obligationItem.tsx index 7eb9517..bdc8cee 100644 --- a/src/views/dashboard/obligationItem.tsx +++ b/src/views/dashboard/obligationItem.tsx @@ -2,11 +2,7 @@ import React from "react"; import { useTokenName } from "../../hooks"; import { LendingObligation, LendingReserve } from "../../models/lending"; import { TokenIcon } from "../../components/TokenIcon"; -import { - wadToLamports, - formatNumber, - fromLamports, -} from "../../utils/utils"; +import { wadToLamports, formatNumber, fromLamports } from "../../utils/utils"; import { Button, Card } from "antd"; import { Link } from "react-router-dom"; import { cache, ParsedAccount, useMint } from "../../contexts/accounts"; @@ -30,9 +26,7 @@ export const ObligationItem = (props: { ); return ( - +
diff --git a/src/views/home/item.tsx b/src/views/home/item.tsx index 4c71f29..350d611 100644 --- a/src/views/home/item.tsx +++ b/src/views/home/item.tsx @@ -1,13 +1,17 @@ -import React from "react"; +import React, { useMemo } from "react"; import { useTokenName } from "../../hooks"; import { calculateBorrowAPY, LendingReserve } from "../../models/lending"; import { TokenIcon } from "../../components/TokenIcon"; -import { wadToLamports, formatNumber, fromLamports } from "../../utils/utils"; +import { + wadToLamports, + formatNumber, + fromLamports, + formatPct, +} from "../../utils/utils"; import { Card } from "antd"; import { Link } from "react-router-dom"; import { PublicKey } from "@solana/web3.js"; import { useMint } from "../../contexts/accounts"; -import { WAD } from "../../constants"; export const LendingReserveItem = (props: { reserve: LendingReserve; @@ -22,10 +26,15 @@ export const LendingReserveItem = (props: { liquidityMint ); - console.log(props.reserve.totalBorrowsWad.toString()); - const totalBorrows = fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint); + const totalBorrows = useMemo( + () => + fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint), + [props.reserve, liquidityMint] + ); - console.log(liquidityMint); + const borrowAPY = useMemo(() => calculateBorrowAPY(props.reserve), [ + props.reserve, + ]); return ( @@ -36,18 +45,15 @@ export const LendingReserveItem = (props: { {name}
- {formatNumber.format(totalLiquidity)} {name} + {formatNumber.format(totalLiquidity+totalBorrows)} {name}
{formatNumber.format(totalBorrows)} {name}
--
-
{calculateBorrowAPY(props.reserve)}
+
{formatPct.format(borrowAPY)}
); }; - - - diff --git a/src/views/repayReserve/index.tsx b/src/views/repayReserve/index.tsx index 31a94b6..29aa820 100644 --- a/src/views/repayReserve/index.tsx +++ b/src/views/repayReserve/index.tsx @@ -15,9 +15,11 @@ export const RepayReserveView = () => { reserve?: string; obligation?: string; }>(); - + const lendingObligation = useLendingObligation(obligationId); - const lendingReserve = useLendingReserve(obligationId ? lendingObligation?.info.borrowReserve : reserveId); + const lendingReserve = useLendingReserve( + obligationId ? lendingObligation?.info.borrowReserve : reserveId + ); const reserve = lendingReserve?.info; console.log([reserveId, obligationId]);