feat: select liquidable loans

This commit is contained in:
Sebastian.Bor 2020-12-13 01:30:58 +00:00
parent ccc78fc6ae
commit db67a387d6
6 changed files with 106 additions and 40 deletions

View File

@ -27,11 +27,14 @@ export const LABELS = {
BORROW_QUESTION: "How much would you like to borrow?", BORROW_QUESTION: "How much would you like to borrow?",
BORROW_ACTION: "Borrow", BORROW_ACTION: "Borrow",
LIQUIDATE_ACTION: "Liquidate", LIQUIDATE_ACTION: "Liquidate",
LIQUIDATE_INFO: "Connect to a wallet to view liquidable loans.",
LIQUIDATE_NO_LOANS: "There are no loans to liquidate with your wallet.",
TABLE_TITLE_ASSET: "Asset", TABLE_TITLE_ASSET: "Asset",
TABLE_TITLE_YOUR_LOAN_BALANCE: "Your loan balance", TABLE_TITLE_YOUR_LOAN_BALANCE: "Your loan balance",
TABLE_TITLE_LOAN_BALANCE: "Loan balance", TABLE_TITLE_LOAN_BALANCE: "Loan balance",
TABLE_TITLE_DEPOSIT_BALANCE: "Your deposit balance", TABLE_TITLE_DEPOSIT_BALANCE: "Your deposit balance",
TABLE_TITLE_APY: "APY", TABLE_TITLE_APY: "APY",
TABLE_TITLE_LTV: "LTV",
TABLE_TITLE_BORROW_APY: "Borrow APY", TABLE_TITLE_BORROW_APY: "Borrow APY",
TABLE_TITLE_DEPOSIT_APY: "Deposit APY", TABLE_TITLE_DEPOSIT_APY: "Deposit APY",
TABLE_TITLE_TOTAL_BORROWED: "Total Borrowed", TABLE_TITLE_TOTAL_BORROWED: "Total Borrowed",

View File

@ -10,3 +10,4 @@ export * from "./useUserObligationByReserve";
export * from "./useBorrowedAmount"; export * from "./useBorrowedAmount";
export * from "./useUserDeposits"; export * from "./useUserDeposits";
export * from "./useSliderInput"; export * from "./useSliderInput";
export * from "./useLiquidableObligations";

View File

@ -0,0 +1,54 @@
import { useMemo } from "react";
import { useUserAccounts } from "./useUserAccounts";
import { useLendingObligations } from "./useLendingObligations";
import { LendingReserve } from "../models/lending";
import { useLendingReserves } from "./useLendingReserves";
import { ParsedAccount } from "../contexts/accounts";
export const useLiquidableObligations = () => {
const { userAccounts } = useUserAccounts();
const { obligations } = useLendingObligations();
const { reserveAccounts } = useLendingReserves();
const availableReserves = useMemo(() => {
return reserveAccounts.reduce((map, reserve) => {
if (userAccounts.some(acc => acc.info.mint.toBase58() === reserve.info.liquidityMint.toBase58())) {
map.set(reserve.pubkey.toBase58(), reserve);
}
return map;
}, new Map<string, ParsedAccount<LendingReserve>>())
}, [reserveAccounts, userAccounts])
const liquidableObligations = useMemo(() => {
if (availableReserves.size === 0) {
return [];
}
return obligations
.map(obligation => (
{
obligation,
reserve: availableReserves.get(obligation.info.borrowReserve.toBase58()) as ParsedAccount<LendingReserve>
}
))
.filter(item => item.reserve)
.map(item => {
// TODO: calculate LTV
const ltv = 81;
const liquidationThreshold = item.reserve.info.config.liquidationThreshold;
const health = (ltv - liquidationThreshold) / liquidationThreshold
return {
obligation: item.obligation,
ltv,
liquidationThreshold,
health
}
})
.filter(item => item.ltv > item.liquidationThreshold)
.sort((a, b) => b.health - a.health);
}, [obligations, availableReserves]);
return {
liquidableObligations
};
}

View File

@ -1,44 +1,36 @@
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import React from "react"; import React from "react";
import { LABELS, ZERO } from "../../constants"; import { LABELS } from "../../constants";
import { LiquidateItem } from "./item"; import { LiquidateItem } from "./item";
import "./itemStyle.less"; import { useLiquidableObligations } from "./../../hooks";
import { useWallet } from "../../contexts/wallet";
import "./style.less";
export const LiquidateView = () => { export const LiquidateView = () => {
const { liquidableObligations } = useLiquidableObligations();
const { connected } = useWallet();
// ParsedAccount<LendingObligation>
const obligations = [
{
pubkey: new PublicKey("2KfJP7pZ6QSpXa26RmsN6kKVQteDEdQmizLSvuyryeiW"),
account: {
executable: false,
owner: new PublicKey("2KfJP7pZ6QSpXa26RmsN6kKVQteDEdQmizLSvuyryeiW"),
lamports: 0,
data: new Buffer("x"),
},
info: {
lastUpdateSlot: ZERO,
collateralAmount: ZERO,
collateralSupply: new PublicKey("2KfJP7pZ6QSpXa26RmsN6kKVQteDEdQmizLSvuyryeiW"),
cumulativeBorrowRateWad: ZERO,
borrowAmountWad: new BN(0),
borrowReserve: new PublicKey("EwhnKnkwcAeVxHDbR5wMpjwipHFuafxTUhQaaagjUxQG"),
tokenMint: new PublicKey("2KfJP7pZ6QSpXa26RmsN6kKVQteDEdQmizLSvuyryeiW"),
}
}
];
return ( return (
<div className="flexColumn"> <div className="liquidate-container">
<div className="liquidate-item liquidate-header"> {!connected && (
<div>{LABELS.TABLE_TITLE_ASSET}</div> <div className="liquidate-info">{LABELS.LIQUIDATE_INFO}</div>
<div>{LABELS.TABLE_TITLE_LOAN_BALANCE}</div> )}
<div>{LABELS.TABLE_TITLE_APY}</div> {connected && liquidableObligations.length === 0 && (
<div>{LABELS.TABLE_TITLE_ACTION}</div> <div className="liquidate-info">{LABELS.LIQUIDATE_NO_LOANS}</div>
</div> )}
{obligations.map((obligation) => ( {connected && liquidableObligations.length > 0 && (
<LiquidateItem key={obligation.pubkey.toBase58()} obligation={obligation}></LiquidateItem> <div className="flexColumn">
))} <div className="liquidate-item liquidate-header">
<div>{LABELS.TABLE_TITLE_ASSET}</div>
<div>{LABELS.TABLE_TITLE_LOAN_BALANCE}</div>
<div>{LABELS.TABLE_TITLE_APY}</div>
<div>{LABELS.TABLE_TITLE_LTV}</div>
<div>{LABELS.TABLE_TITLE_ACTION}</div>
</div>
{liquidableObligations.map((item) => (
<LiquidateItem key={item.obligation.pubkey.toBase58()} obligation={item.obligation} ltv={item.ltv}></LiquidateItem>
))}
</div>
)}
</div> </div>
); );
}; };

View File

@ -1,4 +1,4 @@
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { cache, ParsedAccount, useMint } from "../../contexts/accounts"; import { cache, ParsedAccount, useMint } from "../../contexts/accounts";
import { LendingObligation, LendingReserve, calculateBorrowAPY } from "../../models/lending"; import { LendingObligation, LendingReserve, calculateBorrowAPY } from "../../models/lending";
import { useTokenName } from "../../hooks"; import { useTokenName } from "../../hooks";
@ -15,16 +15,15 @@ import { LABELS } from "../../constants";
export const LiquidateItem = (props: { export const LiquidateItem = (props: {
obligation: ParsedAccount<LendingObligation>; obligation: ParsedAccount<LendingObligation>;
ltv: number
}) => { }) => {
const { obligation } = props; const { obligation, ltv } = props;
const borrowReserve = cache.get(obligation.info.borrowReserve) as ParsedAccount<LendingReserve>; const borrowReserve = cache.get(obligation.info.borrowReserve) as ParsedAccount<LendingReserve>;
const tokenName = useTokenName(borrowReserve?.info.liquidityMint); const tokenName = useTokenName(borrowReserve?.info.liquidityMint);
const liquidityMint = useMint(borrowReserve.info.liquidityMint); const liquidityMint = useMint(borrowReserve.info.liquidityMint);
console.log("wad",obligation.info.borrowAmountWad)
const borrowAmount = fromLamports( const borrowAmount = fromLamports(
wadToLamports(obligation.info.borrowAmountWad), wadToLamports(obligation.info.borrowAmountWad),
liquidityMint liquidityMint
@ -35,7 +34,7 @@ export const LiquidateItem = (props: {
]); ]);
return ( return (
<Link to={`/liquidate/${obligation.pubkey.toBase58()}`}> <Link to={`/liquidate/${borrowReserve.pubkey.toBase58()}`}>
<Card> <Card>
<div className="liquidate-item"> <div className="liquidate-item">
<span style={{ display: "flex" }}> <span style={{ display: "flex" }}>
@ -48,6 +47,9 @@ export const LiquidateItem = (props: {
<div> <div>
{formatPct.format(borrowAPY)} {formatPct.format(borrowAPY)}
</div> </div>
<div>
{formatPct.format(ltv / 100)}
</div>
<div> <div>
<Button> <Button>
<span>{LABELS.LIQUIDATE_ACTION}</span> <span>{LABELS.LIQUIDATE_ACTION}</span>

View File

@ -27,3 +27,17 @@
flex: 80px flex: 80px
} }
} }
.liquidate-info {
display: flex;
align-self: center;
justify-content: center;
flex: 1;
}
.liquidate-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex: 1;
}