feat: add APR/APY and utilization
This commit is contained in:
parent
6bd3967c36
commit
3c3ba90ffc
|
@ -1,11 +1,12 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTokenName } from "./../../hooks";
|
import { useTokenName } from "./../../hooks";
|
||||||
import { LendingReserve } from "../../models/lending";
|
import { calculateBorrowAPR, calculateDepositAPY, calculateUtilizationRatio, LendingReserve } from "../../models/lending";
|
||||||
import { TokenIcon } from "../../components/TokenIcon";
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
import { formatNumber, formatPct, fromLamports } from "../../utils/utils";
|
import { formatNumber, formatPct, fromLamports, wadToLamports } from "../../utils/utils";
|
||||||
import { Card, Typography } from "antd";
|
import { Card, Typography } from "antd";
|
||||||
import { ParsedAccount, useMint } from "../../contexts/accounts";
|
import { ParsedAccount, useMint } from "../../contexts/accounts";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { LABELS } from "../../constants";
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
@ -29,11 +30,10 @@ export const SideReserveOverview = (props: {
|
||||||
liquidityMint
|
liquidityMint
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: calculate
|
const depositApy = calculateDepositAPY(reserve);
|
||||||
const depositApy = 0.048;
|
const borrowApr = calculateBorrowAPR(reserve);
|
||||||
const borrowApy = 0.048;
|
|
||||||
|
|
||||||
const utilizationRate = reserve.config.loanToValueRatio / 100;
|
const utilizationRate = calculateUtilizationRatio(reserve);
|
||||||
const liquidiationThreshold = reserve.config.optimalUtilizationRate / 100;
|
const liquidiationThreshold = reserve.config.optimalUtilizationRate / 100;
|
||||||
const liquidiationPenalty = reserve.config.liquidationBonus / 100;
|
const liquidiationPenalty = reserve.config.liquidationBonus / 100;
|
||||||
const maxLTV = liquidiationThreshold - liquidiationPenalty;
|
const maxLTV = liquidiationThreshold - liquidiationPenalty;
|
||||||
|
@ -44,7 +44,7 @@ export const SideReserveOverview = (props: {
|
||||||
<>
|
<>
|
||||||
<div className="card-row">
|
<div className="card-row">
|
||||||
<Text type="secondary" className="card-cell ">
|
<Text type="secondary" className="card-cell ">
|
||||||
Deposit APY:
|
{LABELS.TABLE_TITLE_DEPOSIT_APR}:
|
||||||
</Text>
|
</Text>
|
||||||
<div className="card-cell ">{formatPct.format(depositApy)}</div>
|
<div className="card-cell ">{formatPct.format(depositApy)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,9 +80,9 @@ export const SideReserveOverview = (props: {
|
||||||
<>
|
<>
|
||||||
<div className="card-row">
|
<div className="card-row">
|
||||||
<Text type="secondary" className="card-cell ">
|
<Text type="secondary" className="card-cell ">
|
||||||
Borrow APY:
|
{LABELS.TABLE_TITLE_BORROW_APR}:
|
||||||
</Text>
|
</Text>
|
||||||
<div className="card-cell ">{formatPct.format(borrowApy)}</div>
|
<div className="card-cell ">{formatPct.format(borrowApr)}</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,4 +38,5 @@ export const LABELS = {
|
||||||
WITHDRAW_QUESTION: "How much would you like to withdraw?",
|
WITHDRAW_QUESTION: "How much would you like to withdraw?",
|
||||||
DASHBOARD_ACTION: "Go to dashboard",
|
DASHBOARD_ACTION: "Go to dashboard",
|
||||||
GO_BACK_ACTION: "Go back",
|
GO_BACK_ACTION: "Go back",
|
||||||
|
DEPOSIT_ACTION: "Deposit",
|
||||||
};
|
};
|
||||||
|
|
|
@ -111,27 +111,27 @@ export const borrowInstruction = (
|
||||||
|
|
||||||
// deposit APY utilization currentUtilizationRate * borrowAPY
|
// deposit APY utilization currentUtilizationRate * borrowAPY
|
||||||
|
|
||||||
export const calculateBorrowAPY = (reserve: LendingReserve) => {
|
export const calculateBorrowAPR = (reserve: LendingReserve) => {
|
||||||
const totalBorrows = reserve.borrowedLiquidityWad.div(WAD).toNumber();
|
const totalBorrows = reserve.borrowedLiquidityWad.div(WAD).toNumber();
|
||||||
const currentUtilization =
|
const currentUtilization =
|
||||||
totalBorrows / (reserve.availableLiqudity.toNumber() + totalBorrows);
|
totalBorrows / (reserve.availableLiqudity.toNumber() + totalBorrows);
|
||||||
const optimalUtilization = reserve.config.optimalUtilizationRate;
|
const optimalUtilization = reserve.config.optimalUtilizationRate;
|
||||||
let borrowAPY;
|
let borrowAPR;
|
||||||
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 =
|
borrowAPR =
|
||||||
normalized_factor * (optimalBorrowRate - minBorrowRate) + minBorrowRate;
|
normalized_factor * (optimalBorrowRate - minBorrowRate) + minBorrowRate;
|
||||||
} else {
|
} else {
|
||||||
const normalized_factor =
|
const normalized_factor =
|
||||||
(currentUtilization - optimalUtilization) / (100 - optimalUtilization);
|
(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 =
|
borrowAPR =
|
||||||
normalized_factor * (maxBorrowRate - optimalBorrowRate) +
|
normalized_factor * (maxBorrowRate - optimalBorrowRate) +
|
||||||
optimalBorrowRate;
|
optimalBorrowRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
return borrowAPY;
|
return borrowAPR;
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ import * as BufferLayout from "buffer-layout";
|
||||||
import { WAD } from "../../constants";
|
import { WAD } from "../../constants";
|
||||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
||||||
import * as Layout from "./../../utils/layout";
|
import * as Layout from "./../../utils/layout";
|
||||||
import { calculateBorrowAPY } from "./borrow";
|
import { calculateBorrowAPR } from "./borrow";
|
||||||
import { LendingInstruction } from "./lending";
|
import { LendingInstruction } from "./lending";
|
||||||
import { LendingReserve } from "./reserve";
|
import { LendingReserve } from "./reserve";
|
||||||
|
|
||||||
|
@ -68,6 +68,6 @@ export const calculateDepositAPY = (reserve: LendingReserve) => {
|
||||||
const currentUtilization =
|
const currentUtilization =
|
||||||
totalBorrows / (reserve.availableLiqudity.toNumber() + totalBorrows);
|
totalBorrows / (reserve.availableLiqudity.toNumber() + totalBorrows);
|
||||||
|
|
||||||
const borrowAPY = calculateBorrowAPY(reserve);
|
const borrowAPY = calculateBorrowAPR(reserve);
|
||||||
return currentUtilization * borrowAPY;
|
return currentUtilization * borrowAPY;
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
import BN from "bn.js";
|
import BN from "bn.js";
|
||||||
import * as BufferLayout from "buffer-layout";
|
import * as BufferLayout from "buffer-layout";
|
||||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
||||||
|
import { wadToLamports } from "../../utils/utils";
|
||||||
import * as Layout from "./../../utils/layout";
|
import * as Layout from "./../../utils/layout";
|
||||||
import { LendingInstruction } from "./lending";
|
import { LendingInstruction } from "./lending";
|
||||||
|
|
||||||
|
@ -168,3 +169,8 @@ export const initReserveInstruction = (
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const calculateUtilizationRatio = (reserve: LendingReserve) => {
|
||||||
|
return reserve.availableLiqudity.toNumber() /
|
||||||
|
(reserve.availableLiqudity.toNumber() + wadToLamports(reserve.borrowedLiquidityWad).toNumber());
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useCollateralBalance, useTokenName } from "../../hooks";
|
import { useCollateralBalance, useTokenName } from "../../hooks";
|
||||||
import { LendingReserve } from "../../models/lending";
|
import { calculateBorrowAPR, LendingReserve } from "../../models/lending";
|
||||||
import { TokenIcon } from "../../components/TokenIcon";
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
import { formatNumber } from "../../utils/utils";
|
import { formatNumber, formatPct } 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 { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
import { LABELS } from "../../constants";
|
||||||
|
|
||||||
export const BorrowItem = (props: {
|
export const BorrowItem = (props: {
|
||||||
reserve: LendingReserve;
|
reserve: LendingReserve;
|
||||||
|
@ -16,6 +17,8 @@ export const BorrowItem = (props: {
|
||||||
// TODO: calculate avilable amount... based on total owned collateral across all the reserves
|
// TODO: calculate avilable amount... based on total owned collateral across all the reserves
|
||||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
||||||
|
|
||||||
|
const apr = calculateBorrowAPR(props.reserve);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/borrow/${props.address.toBase58()}`}>
|
<Link to={`/borrow/${props.address.toBase58()}`}>
|
||||||
<Card>
|
<Card>
|
||||||
|
@ -27,10 +30,10 @@ export const BorrowItem = (props: {
|
||||||
<div>
|
<div>
|
||||||
{formatNumber.format(collateralBalance)} {name}
|
{formatNumber.format(collateralBalance)} {name}
|
||||||
</div>
|
</div>
|
||||||
<div>--</div>
|
<div>{formatPct.format(apr)}</div>
|
||||||
<div>
|
<div>
|
||||||
<Button>
|
<Button>
|
||||||
<span>Borrow</span>
|
<span>{LABELS.BORROW_ACTION}</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,12 +4,13 @@ import {
|
||||||
useTokenName,
|
useTokenName,
|
||||||
useUserBalance,
|
useUserBalance,
|
||||||
} from "../../../hooks";
|
} from "../../../hooks";
|
||||||
import { LendingReserve } from "../../../models/lending";
|
import { calculateDepositAPY, LendingReserve } from "../../../models/lending";
|
||||||
import { TokenIcon } from "../../../components/TokenIcon";
|
import { TokenIcon } from "../../../components/TokenIcon";
|
||||||
import { formatNumber } from "../../../utils/utils";
|
import { formatNumber, formatPct } 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 { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
import { LABELS } from "../../../constants";
|
||||||
|
|
||||||
export const ReserveItem = (props: {
|
export const ReserveItem = (props: {
|
||||||
reserve: LendingReserve;
|
reserve: LendingReserve;
|
||||||
|
@ -19,6 +20,8 @@ export const ReserveItem = (props: {
|
||||||
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
||||||
|
|
||||||
|
const apy = calculateDepositAPY(props.reserve);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/deposit/${props.address.toBase58()}`}>
|
<Link to={`/deposit/${props.address.toBase58()}`}>
|
||||||
<Card>
|
<Card>
|
||||||
|
@ -33,10 +36,10 @@ export const ReserveItem = (props: {
|
||||||
<div>
|
<div>
|
||||||
{formatNumber.format(collateralBalance)} {name}
|
{formatNumber.format(collateralBalance)} {name}
|
||||||
</div>
|
</div>
|
||||||
<div>--</div>
|
<div>{formatPct.format(apy)}</div>
|
||||||
<div>
|
<div>
|
||||||
<Button>
|
<Button>
|
||||||
<span>Deposit</span>
|
<span>{LABELS.DEPOSIT_ACTION}</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { useTokenName } from "../../hooks";
|
import { useTokenName } from "../../hooks";
|
||||||
import {
|
import {
|
||||||
calculateBorrowAPY,
|
calculateBorrowAPR,
|
||||||
calculateDepositAPY,
|
calculateDepositAPY,
|
||||||
LendingReserve,
|
LendingReserve,
|
||||||
} from "../../models/lending";
|
} from "../../models/lending";
|
||||||
|
@ -39,7 +39,7 @@ export const LendingReserveItem = (props: {
|
||||||
[props.reserve, liquidityMint]
|
[props.reserve, liquidityMint]
|
||||||
);
|
);
|
||||||
|
|
||||||
const borrowAPY = useMemo(() => calculateBorrowAPY(props.reserve), [
|
const borrowAPY = useMemo(() => calculateBorrowAPR(props.reserve), [
|
||||||
props.reserve,
|
props.reserve,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue