feat: add APR/APY and utilization

This commit is contained in:
bartosz-lipinski 2020-11-26 01:18:18 -06:00
parent 6bd3967c36
commit 3c3ba90ffc
8 changed files with 39 additions and 26 deletions

View File

@ -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>
</> </>
); );

View File

@ -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",
}; };

View File

@ -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;
}; };

View File

@ -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;
}; };

View File

@ -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());
}

View File

@ -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>

View File

@ -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>

View File

@ -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,
]); ]);