feat: add liquidity chart
This commit is contained in:
parent
31020e7e86
commit
f9adc64ca9
|
@ -20,4 +20,5 @@ Any content produced by Solana, or developer resources that Solana provides, are
|
||||||
- [] Add market size on front page
|
- [] Add market size on front page
|
||||||
- [] Add github link
|
- [] Add github link
|
||||||
- [] Repay from reserve (add selection for obligation/loan)
|
- [] Repay from reserve (add selection for obligation/loan)
|
||||||
- []
|
- [] Add support for token names in URL in addition to reserve address
|
||||||
|
|
||||||
|
|
|
@ -76,8 +76,8 @@ export const repay = async (
|
||||||
signers
|
signers
|
||||||
);
|
);
|
||||||
|
|
||||||
const loanRatio = amountLamports / wadToLamports(obligation.info.borrowAmountWad)
|
const loanRatio =
|
||||||
.toNumber();
|
amountLamports / wadToLamports(obligation.info.borrowAmountWad).toNumber();
|
||||||
console.log(loanRatio);
|
console.log(loanRatio);
|
||||||
|
|
||||||
// create approval for transfer transactions
|
// create approval for transfer transactions
|
||||||
|
|
|
@ -84,9 +84,7 @@ export const BorrowInput = (props: {
|
||||||
justifyContent: "space-around",
|
justifyContent: "space-around",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="borrow-input-title">
|
<div className="borrow-input-title">{LABELS.BORROW_QUESTION}</div>
|
||||||
{LABELS.BORROW_QUESTION}
|
|
||||||
</div>
|
|
||||||
<div className="token-input">
|
<div className="token-input">
|
||||||
<TokenIcon mintAddress={borrowReserve?.liquidityMint} />
|
<TokenIcon mintAddress={borrowReserve?.liquidityMint} />
|
||||||
<NumericInput
|
<NumericInput
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import { Button } from "antd"
|
import { Button } from "antd";
|
||||||
import { ButtonProps } from "antd/lib/button"
|
import { ButtonProps } from "antd/lib/button";
|
||||||
import React from "react"
|
import React from "react";
|
||||||
import { useWallet } from "../../contexts/wallet";
|
import { useWallet } from "../../contexts/wallet";
|
||||||
import { LABELS } from './../../constants';
|
import { LABELS } from "./../../constants";
|
||||||
|
|
||||||
export const ConnectButton = (props: ButtonProps & React.RefAttributes<HTMLElement>) => {
|
export const ConnectButton = (
|
||||||
|
props: ButtonProps & React.RefAttributes<HTMLElement>
|
||||||
|
) => {
|
||||||
const { wallet, connected } = useWallet();
|
const { wallet, connected } = useWallet();
|
||||||
const { onClick, children, ...rest } = props;
|
const { onClick, children, ...rest } = props;
|
||||||
return <Button {...rest} onClick={connected ? onClick : wallet.connect} >
|
return (
|
||||||
{connected ? props.children : LABELS.CONNECT_LABEL}
|
<Button {...rest} onClick={connected ? onClick : wallet.connect}>
|
||||||
</Button>
|
{connected ? props.children : LABELS.CONNECT_LABEL}
|
||||||
}
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -36,9 +36,7 @@ export const AppLayout = (props: any) => {
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<div className="Banner">
|
<div className="Banner">
|
||||||
<div className="Banner-description">
|
<div className="Banner-description">{LABELS.AUDIT_WARNING}</div>
|
||||||
{LABELS.AUDIT_WARNING}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<BasicLayout
|
<BasicLayout
|
||||||
title={LABELS.APP_TITLE}
|
title={LABELS.APP_TITLE}
|
||||||
|
|
|
@ -14,7 +14,12 @@ import { useWallet } from "../../contexts/wallet";
|
||||||
import { repay } from "../../actions";
|
import { repay } from "../../actions";
|
||||||
import { CollateralSelector } from "./../CollateralSelector";
|
import { CollateralSelector } from "./../CollateralSelector";
|
||||||
import "./style.less";
|
import "./style.less";
|
||||||
import { wadToLamports, formatNumber, fromLamports, toLamports } from "../../utils/utils";
|
import {
|
||||||
|
wadToLamports,
|
||||||
|
formatNumber,
|
||||||
|
fromLamports,
|
||||||
|
toLamports,
|
||||||
|
} from "../../utils/utils";
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
|
|
||||||
export const RepayInput = (props: {
|
export const RepayInput = (props: {
|
||||||
|
@ -50,9 +55,14 @@ export const RepayInput = (props: {
|
||||||
|
|
||||||
const obligationAccount = useAccountByMint(obligation?.info.tokenMint);
|
const obligationAccount = useAccountByMint(obligation?.info.tokenMint);
|
||||||
|
|
||||||
const lamports = useMemo(() => 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(() => {
|
const onRepay = useCallback(() => {
|
||||||
if (
|
if (
|
||||||
|
@ -103,8 +113,8 @@ export const RepayInput = (props: {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="repay-input-title">
|
<div className="repay-input-title">
|
||||||
{LABELS.REPAY_QUESTION} (Currently:{" "})
|
{LABELS.REPAY_QUESTION} (Currently: ){formatNumber.format(balance)}{" "}
|
||||||
{formatNumber.format(balance)} {name})
|
{name})
|
||||||
</div>
|
</div>
|
||||||
<div className="token-input">
|
<div className="token-input">
|
||||||
<TokenIcon mintAddress={repayReserve?.info.liquidityMint} />
|
<TokenIcon mintAddress={repayReserve?.info.liquidityMint} />
|
||||||
|
@ -124,10 +134,22 @@ export const RepayInput = (props: {
|
||||||
/>
|
/>
|
||||||
<div>{name}</div>
|
<div>{name}</div>
|
||||||
</div>
|
</div>
|
||||||
<Slider marks={marks}
|
<Slider
|
||||||
value={mark}
|
marks={marks}
|
||||||
onChange={(val: number) =>
|
value={mark}
|
||||||
setValue((fromLamports(wadToLamports(obligation?.info.borrowAmountWad).toNumber(), repayLiquidityMint) * val / 100).toFixed(2))} />
|
onChange={(val: number) =>
|
||||||
|
setValue(
|
||||||
|
(
|
||||||
|
(fromLamports(
|
||||||
|
wadToLamports(obligation?.info.borrowAmountWad).toNumber(),
|
||||||
|
repayLiquidityMint
|
||||||
|
) *
|
||||||
|
val) /
|
||||||
|
100
|
||||||
|
).toFixed(2)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
<div className="repay-input-title">{LABELS.SELECT_COLLATERAL}</div>
|
<div className="repay-input-title">{LABELS.SELECT_COLLATERAL}</div>
|
||||||
<CollateralSelector
|
<CollateralSelector
|
||||||
reserve={repayReserve.info}
|
reserve={repayReserve.info}
|
||||||
|
@ -148,9 +170,9 @@ export const RepayInput = (props: {
|
||||||
};
|
};
|
||||||
|
|
||||||
const marks = {
|
const marks = {
|
||||||
0: '0%',
|
0: "0%",
|
||||||
25: '25%',
|
25: "25%",
|
||||||
50: '50%',
|
50: "50%",
|
||||||
75: '75%',
|
75: "75%",
|
||||||
100: '100%'
|
100: "100%",
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,115 @@
|
||||||
import React from "react";
|
import React, { useEffect, useMemo, useRef } from "react";
|
||||||
import { LendingReserve } from "../../models/lending";
|
import { LendingReserve } from "../../models/lending";
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import "./style.less";
|
import "./style.less";
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
|
import echarts from "echarts";
|
||||||
|
import { formatNumber, formatUSD, fromLamports, wadToLamports } from "../../utils/utils";
|
||||||
|
import { useMint } from "../../contexts/accounts";
|
||||||
|
|
||||||
|
export const ReserveUtilizationChart = (props: {
|
||||||
|
reserve: LendingReserve;
|
||||||
|
}) => {
|
||||||
|
const chartDiv = useRef<HTMLDivElement>(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 <div ref={chartDiv} style={{ height: 300, width: 400 }} />
|
||||||
|
}
|
||||||
|
|
||||||
export const ReserveStatus = (props: {
|
export const ReserveStatus = (props: {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -29,7 +135,7 @@ export const ReserveStatus = (props: {
|
||||||
justifyContent: "space-around",
|
justifyContent: "space-around",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
TODO: Reserve Status - add chart
|
<ReserveUtilizationChart reserve={props.reserve} />
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,12 +6,10 @@ import {
|
||||||
useBorrowedAmount,
|
useBorrowedAmount,
|
||||||
} from "./../../hooks";
|
} from "./../../hooks";
|
||||||
import { LendingReserve } from "../../models/lending";
|
import { LendingReserve } from "../../models/lending";
|
||||||
import { formatNumber, wadToLamports } from "../../utils/utils";
|
import { formatNumber } from "../../utils/utils";
|
||||||
import { Button, Card, Typography } from "antd";
|
import { Button, Card, Typography } from "antd";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import { cache, ParsedAccount } from "../../contexts/accounts";
|
|
||||||
import { MintInfo } from "@solana/spl-token";
|
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
|
|
@ -76,9 +76,7 @@ export const WithdrawInput = (props: {
|
||||||
justifyContent: "space-around",
|
justifyContent: "space-around",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="withdraw-input-title">
|
<div className="withdraw-input-title">{LABELS.WITHDRAW_QUESTION}</div>
|
||||||
{LABELS.WITHDRAW_QUESTION}
|
|
||||||
</div>
|
|
||||||
<div className="token-input">
|
<div className="token-input">
|
||||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||||
<NumericInput
|
<NumericInput
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export * from './ids';
|
export * from "./ids";
|
||||||
export * from './labels';
|
export * from "./labels";
|
||||||
export * from './math';
|
export * from "./math";
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
export const LABELS = {
|
export const LABELS = {
|
||||||
CONNECT_LABEL: "Connect Wallet",
|
CONNECT_LABEL: "Connect Wallet",
|
||||||
GIVE_SOL: "Give me SOL",
|
GIVE_SOL: "Give me SOL",
|
||||||
FAUCET_INFO: "This faucet will help you fund your accounts outside of Solana main network.",
|
FAUCET_INFO:
|
||||||
|
"This faucet will help you fund your accounts outside of Solana main network.",
|
||||||
ACCOUNT_FUNDED: "Account funded.",
|
ACCOUNT_FUNDED: "Account funded.",
|
||||||
REPAY_QUESTION: "How much would you like to repay?",
|
REPAY_QUESTION: "How much would you like to repay?",
|
||||||
REPAY_ACTION: "Repay",
|
REPAY_ACTION: "Repay",
|
||||||
RESERVE_STATUS_TITLE: "Reserve Status & Configuration",
|
RESERVE_STATUS_TITLE: "Reserve Status & Configuration",
|
||||||
AUDIT_WARNING: "Oyster Lending is unaudited software. Use at your own risk.",
|
AUDIT_WARNING: "Oyster Lending is unaudited software. Use at your own risk.",
|
||||||
MENU_HOME: "Home",
|
MENU_HOME: "Home",
|
||||||
MENU_DASHBOARD: "Dashboard",
|
MENU_DASHBOARD: "Dashboard",
|
||||||
|
@ -27,4 +28,4 @@ export const LABELS = {
|
||||||
DASHBOARD_TITLE_DEPOSITS: "Deposts",
|
DASHBOARD_TITLE_DEPOSITS: "Deposts",
|
||||||
WITHDRAW_ACTION: "Withdraw",
|
WITHDRAW_ACTION: "Withdraw",
|
||||||
WITHDRAW_QUESTION: "How much would you like to withdraw?",
|
WITHDRAW_QUESTION: "How much would you like to withdraw?",
|
||||||
}
|
};
|
||||||
|
|
|
@ -3,4 +3,4 @@ import BN from "bn.js";
|
||||||
export const TEN = new BN(10);
|
export const TEN = new BN(10);
|
||||||
export const WAD = TEN.pow(new BN(18));
|
export const WAD = TEN.pow(new BN(18));
|
||||||
export const RAY = TEN.pow(new BN(27));
|
export const RAY = TEN.pow(new BN(27));
|
||||||
export const ZERO = new BN(0);
|
export const ZERO = new BN(0);
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useUserAccounts } from "./useUserAccounts";
|
|
||||||
import { useLendingObligations } from "./useLendingObligations";
|
|
||||||
import { TokenAccount } from "../models";
|
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import { useUserObligationByReserve } from "./useUserObligationByReserve";
|
import { useUserObligationByReserve } from "./useUserObligationByReserve";
|
||||||
import { fromLamports, wadToLamports } from "../utils/utils";
|
import { fromLamports, wadToLamports } from "../utils/utils";
|
||||||
import { cache, getMultipleAccounts, MintParser, ParsedAccount, useMint } from "../contexts/accounts";
|
import {
|
||||||
|
cache,
|
||||||
|
getMultipleAccounts,
|
||||||
|
MintParser,
|
||||||
|
ParsedAccount,
|
||||||
|
useMint,
|
||||||
|
} from "../contexts/accounts";
|
||||||
import { useConnection } from "../contexts/connection";
|
import { useConnection } from "../contexts/connection";
|
||||||
import { MintInfo } from "@solana/spl-token";
|
import { MintInfo } from "@solana/spl-token";
|
||||||
import { useLendingReserve } from "./useLendingReserves";
|
import { useLendingReserve } from "./useLendingReserves";
|
||||||
|
@ -24,30 +27,40 @@ export function useBorrowedAmount(address?: string | PublicKey) {
|
||||||
// precache obligation mints
|
// precache obligation mints
|
||||||
const { keys, array } = await getMultipleAccounts(
|
const { keys, array } = await getMultipleAccounts(
|
||||||
connection,
|
connection,
|
||||||
userObligationsByReserve
|
userObligationsByReserve.map((item) =>
|
||||||
.map(item => item.obligation.info.tokenMint.toBase58()),
|
item.obligation.info.tokenMint.toBase58()
|
||||||
"single");
|
),
|
||||||
|
"single"
|
||||||
|
);
|
||||||
|
|
||||||
array.forEach((item, index) => {
|
array.forEach((item, index) => {
|
||||||
const address = keys[index];
|
const address = keys[index];
|
||||||
cache.add(new PublicKey(address), item, MintParser);
|
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<MintInfo>;
|
||||||
|
|
||||||
const owned = item.userAccounts.reduce((amount, acc) => amount += acc.info.amount.toNumber(), 0);
|
result += (borrowed * owned) / obligationMint?.info.supply.toNumber();
|
||||||
const obligationMint = cache.get(item.obligation.info.tokenMint) as ParsedAccount<MintInfo>;
|
return result;
|
||||||
|
}, 0)
|
||||||
result += borrowed * owned / obligationMint?.info.supply.toNumber();
|
);
|
||||||
return result
|
|
||||||
|
|
||||||
}, 0));
|
|
||||||
})();
|
})();
|
||||||
|
}, [connection, userObligationsByReserve]);
|
||||||
|
|
||||||
|
return {
|
||||||
}, [userObligationsByReserve]);
|
borrowed: fromLamports(borrowedLamports, liquidityMint),
|
||||||
|
borrowedLamports,
|
||||||
return { borrowed: fromLamports(borrowedLamports, liquidityMint), borrowedLamports };
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ export function useLendingReserve(address?: string | PublicKey) {
|
||||||
>();
|
>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setReserveAccount(cache.get(id || ''));
|
setReserveAccount(cache.get(id || ""));
|
||||||
|
|
||||||
const dispose = cache.emitter.onCache((args) => {
|
const dispose = cache.emitter.onCache((args) => {
|
||||||
if (args.id === id) {
|
if (args.id === id) {
|
||||||
|
|
|
@ -5,14 +5,12 @@ import { PublicKey } from "@solana/web3.js";
|
||||||
export function useUserObligationByReserve(reserve?: string | PublicKey) {
|
export function useUserObligationByReserve(reserve?: string | PublicKey) {
|
||||||
const { userObligations } = useUserObligations();
|
const { userObligations } = useUserObligations();
|
||||||
|
|
||||||
const userObligationsByReserve = useMemo(
|
const userObligationsByReserve = useMemo(() => {
|
||||||
() => {
|
const id = typeof reserve === "string" ? reserve : reserve?.toBase58();
|
||||||
const id = typeof reserve === 'string' ? reserve : reserve?.toBase58();
|
return userObligations.filter(
|
||||||
return userObligations.filter((item) =>
|
(item) => item.obligation.info.borrowReserve.toBase58() === id
|
||||||
item.obligation.info.borrowReserve.toBase58() === id
|
);
|
||||||
)
|
}, [reserve, userObligations]);
|
||||||
}, [reserve, userObligations]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userObligationsByReserve,
|
userObligationsByReserve,
|
||||||
|
|
|
@ -262,21 +262,26 @@ export const withdrawInstruction = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const calculateBorrowAPY = (reserve: LendingReserve) => {
|
export const calculateBorrowAPY = (reserve: LendingReserve) => {
|
||||||
const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber()
|
const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber();
|
||||||
const currentUtilization = totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows)
|
const currentUtilization =
|
||||||
const optimalUtilization = reserve.config.optimalUtilizationRate
|
totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows);
|
||||||
|
const optimalUtilization = reserve.config.optimalUtilizationRate;
|
||||||
let borrowAPY;
|
let borrowAPY;
|
||||||
if (currentUtilization < optimalUtilization) {
|
if (currentUtilization < optimalUtilization) {
|
||||||
const normalized_factor = currentUtilization / optimalUtilization;
|
const normalized_factor = currentUtilization / optimalUtilization;
|
||||||
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
|
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
|
||||||
const minBorrowRate = reserve.config.minBorrowRate / 100;
|
const minBorrowRate = reserve.config.minBorrowRate / 100;
|
||||||
borrowAPY = normalized_factor * (optimalBorrowRate - minBorrowRate) + minBorrowRate;
|
borrowAPY =
|
||||||
|
normalized_factor * (optimalBorrowRate - minBorrowRate) + minBorrowRate;
|
||||||
} else {
|
} else {
|
||||||
const normalized_factor = (currentUtilization - optimalUtilization) / (100 - optimalUtilization);
|
const normalized_factor =
|
||||||
|
(currentUtilization - optimalUtilization) / (100 - optimalUtilization);
|
||||||
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
|
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
|
||||||
const maxBorrowRate = reserve.config.maxBorrowRate / 100;
|
const maxBorrowRate = reserve.config.maxBorrowRate / 100;
|
||||||
borrowAPY = normalized_factor * (maxBorrowRate - optimalBorrowRate) + optimalBorrowRate;
|
borrowAPY =
|
||||||
|
normalized_factor * (maxBorrowRate - optimalBorrowRate) +
|
||||||
|
optimalBorrowRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
return borrowAPY;
|
return borrowAPY;
|
||||||
}
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
import { useUserObligations } from "./../../hooks";
|
import { useUserObligations } from "./../../hooks";
|
||||||
import { ObligationItem } from "./obligationItem";
|
import { ObligationItem } from "./obligationItem";
|
||||||
import "./style.less"
|
import "./style.less";
|
||||||
|
|
||||||
export const DashboardView = () => {
|
export const DashboardView = () => {
|
||||||
const { userObligations } = useUserObligations();
|
const { userObligations } = useUserObligations();
|
||||||
|
@ -14,12 +14,14 @@ export const DashboardView = () => {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{LABELS.DASHBOARD_TITLE_LOANS}</span>
|
<span>{LABELS.DASHBOARD_TITLE_LOANS}</span>
|
||||||
{userObligations.length > 0 && <div className="dashboard-item dashboard-header">
|
{userObligations.length > 0 && (
|
||||||
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
<div className="dashboard-item dashboard-header">
|
||||||
<div>{LABELS.TABLE_TITLE_LOAN_BALANCE}</div>
|
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
||||||
<div>{LABELS.TABLE_TITLE_APY}</div>
|
<div>{LABELS.TABLE_TITLE_LOAN_BALANCE}</div>
|
||||||
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
<div>{LABELS.TABLE_TITLE_APY}</div>
|
||||||
</div>}
|
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{userObligations.map((item) => {
|
{userObligations.map((item) => {
|
||||||
return <ObligationItem obligation={item.obligation} />;
|
return <ObligationItem obligation={item.obligation} />;
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -2,11 +2,7 @@ import React from "react";
|
||||||
import { useTokenName } from "../../hooks";
|
import { useTokenName } from "../../hooks";
|
||||||
import { LendingObligation, LendingReserve } from "../../models/lending";
|
import { LendingObligation, LendingReserve } from "../../models/lending";
|
||||||
import { TokenIcon } from "../../components/TokenIcon";
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
import {
|
import { wadToLamports, formatNumber, fromLamports } from "../../utils/utils";
|
||||||
wadToLamports,
|
|
||||||
formatNumber,
|
|
||||||
fromLamports,
|
|
||||||
} from "../../utils/utils";
|
|
||||||
import { Button, Card } from "antd";
|
import { Button, Card } from "antd";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { cache, ParsedAccount, useMint } from "../../contexts/accounts";
|
import { cache, ParsedAccount, useMint } from "../../contexts/accounts";
|
||||||
|
@ -30,9 +26,7 @@ export const ObligationItem = (props: {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link to={`/repay/loan/${obligation.pubkey.toBase58()}`}>
|
||||||
to={`/repay/loan/${obligation.pubkey.toBase58()}`}
|
|
||||||
>
|
|
||||||
<Card>
|
<Card>
|
||||||
<div className="dashboard-item">
|
<div className="dashboard-item">
|
||||||
<span style={{ display: "flex" }}>
|
<span style={{ display: "flex" }}>
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import React from "react";
|
import React, { useMemo } from "react";
|
||||||
import { useTokenName } from "../../hooks";
|
import { useTokenName } from "../../hooks";
|
||||||
import { calculateBorrowAPY, LendingReserve } from "../../models/lending";
|
import { calculateBorrowAPY, LendingReserve } from "../../models/lending";
|
||||||
import { TokenIcon } from "../../components/TokenIcon";
|
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 { Card } from "antd";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import { useMint } from "../../contexts/accounts";
|
import { useMint } from "../../contexts/accounts";
|
||||||
import { WAD } from "../../constants";
|
|
||||||
|
|
||||||
export const LendingReserveItem = (props: {
|
export const LendingReserveItem = (props: {
|
||||||
reserve: LendingReserve;
|
reserve: LendingReserve;
|
||||||
|
@ -22,10 +26,15 @@ export const LendingReserveItem = (props: {
|
||||||
liquidityMint
|
liquidityMint
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(props.reserve.totalBorrowsWad.toString());
|
const totalBorrows = useMemo(
|
||||||
const totalBorrows = fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint);
|
() =>
|
||||||
|
fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint),
|
||||||
|
[props.reserve, liquidityMint]
|
||||||
|
);
|
||||||
|
|
||||||
console.log(liquidityMint);
|
const borrowAPY = useMemo(() => calculateBorrowAPY(props.reserve), [
|
||||||
|
props.reserve,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/reserve/${props.address.toBase58()}`}>
|
<Link to={`/reserve/${props.address.toBase58()}`}>
|
||||||
|
@ -36,18 +45,15 @@ export const LendingReserveItem = (props: {
|
||||||
{name}
|
{name}
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
{formatNumber.format(totalLiquidity)} {name}
|
{formatNumber.format(totalLiquidity+totalBorrows)} {name}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{formatNumber.format(totalBorrows)} {name}
|
{formatNumber.format(totalBorrows)} {name}
|
||||||
</div>
|
</div>
|
||||||
<div>--</div>
|
<div>--</div>
|
||||||
<div>{calculateBorrowAPY(props.reserve)}</div>
|
<div>{formatPct.format(borrowAPY)}</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,11 @@ export const RepayReserveView = () => {
|
||||||
reserve?: string;
|
reserve?: string;
|
||||||
obligation?: string;
|
obligation?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const lendingObligation = useLendingObligation(obligationId);
|
const lendingObligation = useLendingObligation(obligationId);
|
||||||
const lendingReserve = useLendingReserve(obligationId ? lendingObligation?.info.borrowReserve : reserveId);
|
const lendingReserve = useLendingReserve(
|
||||||
|
obligationId ? lendingObligation?.info.borrowReserve : reserveId
|
||||||
|
);
|
||||||
const reserve = lendingReserve?.info;
|
const reserve = lendingReserve?.info;
|
||||||
|
|
||||||
console.log([reserveId, obligationId]);
|
console.log([reserveId, obligationId]);
|
||||||
|
|
Loading…
Reference in New Issue