feat: ltv and health factor
This commit is contained in:
parent
d5fab229af
commit
dbf08a5729
|
@ -77,7 +77,7 @@ export const BorrowInput = (props: {
|
||||||
|
|
||||||
// TODO: select exsisting obligations by collateral reserve
|
// TODO: select exsisting obligations by collateral reserve
|
||||||
userObligationsByReserve.length > 0
|
userObligationsByReserve.length > 0
|
||||||
? userObligationsByReserve[0].obligation
|
? userObligationsByReserve[0].obligation.account
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|
||||||
userObligationsByReserve.length > 0
|
userObligationsByReserve.length > 0
|
||||||
|
|
|
@ -4,7 +4,8 @@ import React, { useCallback } from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
import { ParsedAccount } from "../../contexts/accounts";
|
import { ParsedAccount } from "../../contexts/accounts";
|
||||||
import { LendingObligation, LendingReserve } from "../../models";
|
import { EnrichedLendingObligation } from "../../hooks";
|
||||||
|
import { LendingReserve } from "../../models";
|
||||||
import { BackButton } from "../BackButton";
|
import { BackButton } from "../BackButton";
|
||||||
import { CollateralSelector } from "../CollateralSelector";
|
import { CollateralSelector } from "../CollateralSelector";
|
||||||
import "./style.less";
|
import "./style.less";
|
||||||
|
@ -13,7 +14,7 @@ export const LiquidateInput = (props: {
|
||||||
className?: string;
|
className?: string;
|
||||||
reserve: ParsedAccount<LendingReserve>;
|
reserve: ParsedAccount<LendingReserve>;
|
||||||
collateralReserve?: ParsedAccount<LendingReserve>;
|
collateralReserve?: ParsedAccount<LendingReserve>;
|
||||||
obligation: ParsedAccount<LendingObligation>;
|
obligation: EnrichedLendingObligation;
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const { reserve, collateralReserve } = props;
|
const { reserve, collateralReserve } = props;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useCallback, useState } from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
import { InputType, useAccountByMint, useSliderInput, useTokenName, useUserBalance } from "../../hooks";
|
import { EnrichedLendingObligation, InputType, useAccountByMint, useSliderInput, useTokenName, useUserBalance } from "../../hooks";
|
||||||
import {
|
import {
|
||||||
LendingObligation,
|
|
||||||
LendingReserve,
|
LendingReserve,
|
||||||
} from "../../models";
|
} from "../../models";
|
||||||
import { TokenIcon } from "../TokenIcon";
|
import { TokenIcon } from "../TokenIcon";
|
||||||
|
@ -22,7 +21,7 @@ export const RepayInput = (props: {
|
||||||
className?: string;
|
className?: string;
|
||||||
borrowReserve: ParsedAccount<LendingReserve>;
|
borrowReserve: ParsedAccount<LendingReserve>;
|
||||||
collateralReserve?: ParsedAccount<LendingReserve>;
|
collateralReserve?: ParsedAccount<LendingReserve>;
|
||||||
obligation: ParsedAccount<LendingObligation>;
|
obligation: EnrichedLendingObligation;
|
||||||
}) => {
|
}) => {
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
const { wallet } = useWallet();
|
const { wallet } = useWallet();
|
||||||
|
@ -82,7 +81,7 @@ export const RepayInput = (props: {
|
||||||
await repay(
|
await repay(
|
||||||
fromAccounts[0],
|
fromAccounts[0],
|
||||||
toRepayLamports,
|
toRepayLamports,
|
||||||
obligation,
|
obligation.account,
|
||||||
obligationAccount,
|
obligationAccount,
|
||||||
repayReserve,
|
repayReserve,
|
||||||
collateralReserve,
|
collateralReserve,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||||
import { MINT_TO_MARKET } from "./../models/marketOverrides";
|
import { MINT_TO_MARKET } from "./../models/marketOverrides";
|
||||||
import { STABLE_COINS } from "./../utils/utils";
|
import { fromLamports, STABLE_COINS } from "./../utils/utils";
|
||||||
import { useConnectionConfig } from "./connection";
|
import { useConnectionConfig } from "./connection";
|
||||||
import { cache, getMultipleAccounts, ParsedAccount } from "./accounts";
|
import { cache, getMultipleAccounts, ParsedAccount } from "./accounts";
|
||||||
import { Market, MARKETS, Orderbook, TOKEN_MINTS } from "@project-serum/serum";
|
import { Market, MARKETS, Orderbook, TOKEN_MINTS } from "@project-serum/serum";
|
||||||
|
@ -249,8 +249,14 @@ export const usePrecacheMarket = () => {
|
||||||
return context.precacheMarkets;
|
return context.precacheMarkets;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve) => {
|
export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve, dex: PublicKey) => {
|
||||||
const marketInfo = cache.get(reserve?.dexMarket);
|
const liquidityMint = cache.get(reserve.liquidityMint);
|
||||||
|
const collateralMint = cache.get(reserve.collateralMint);
|
||||||
|
if (!liquidityMint || !collateralMint) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const marketInfo = cache.get(dex);
|
||||||
if (!marketInfo) {
|
if (!marketInfo) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
@ -287,13 +293,20 @@ export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve)
|
||||||
const book = new Orderbook(dexMarket, bookInfo.accountFlags, bookInfo.slab);
|
const book = new Orderbook(dexMarket, bookInfo.accountFlags, bookInfo.slab);
|
||||||
|
|
||||||
let cost = 0;
|
let cost = 0;
|
||||||
let remaining = amount;
|
let remaining = fromLamports(amount, liquidityMint.info);
|
||||||
|
|
||||||
if (book) {
|
if (book) {
|
||||||
for (const level of book) {
|
const depth = book.getL2(1000);
|
||||||
let size = remaining > level.size ? level.size : level.size - remaining;
|
let price, sizeAtLevel: number;
|
||||||
cost = cost + level.price * size;
|
|
||||||
remaining = remaining - size;
|
const op = book.isBids ?
|
||||||
|
(price: number, size: number) => size / price :
|
||||||
|
(price: number, size: number) => size * price;
|
||||||
|
|
||||||
|
for ([price, sizeAtLevel] of depth) {
|
||||||
|
let filled = remaining > sizeAtLevel ? sizeAtLevel : remaining;
|
||||||
|
cost = cost + op(price, filled);
|
||||||
|
remaining = remaining - filled;
|
||||||
|
|
||||||
if (remaining <= 0) {
|
if (remaining <= 0) {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -10,4 +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";
|
export * from "./useEnrichedLendingObligations";
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { cache, ParsedAccount } from "./../contexts/accounts";
|
||||||
|
import { useLendingObligations } from "./useLendingObligations";
|
||||||
|
import { collateralToLiquidity, LendingObligation, LendingReserve } from "../models/lending";
|
||||||
|
import { useLendingReserves } from "./useLendingReserves";
|
||||||
|
import { fromLamports, wadToLamports } from "../utils/utils";
|
||||||
|
import { MintInfo } from "@solana/spl-token";
|
||||||
|
import { simulateMarketOrderFill } from "../contexts/market";
|
||||||
|
|
||||||
|
interface EnrichedLendingObligationInfo extends LendingObligation {
|
||||||
|
ltv: number;
|
||||||
|
health: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnrichedLendingObligation {
|
||||||
|
account: ParsedAccount<LendingObligation>;
|
||||||
|
info: EnrichedLendingObligationInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useEnrichedLendingObligations() {
|
||||||
|
const { obligations } = useLendingObligations();
|
||||||
|
const { reserveAccounts } = useLendingReserves();
|
||||||
|
|
||||||
|
const availableReserves = useMemo(() => {
|
||||||
|
return reserveAccounts.reduce((map, reserve) => {
|
||||||
|
map.set(reserve.pubkey.toBase58(), reserve);
|
||||||
|
return map;
|
||||||
|
}, new Map<string, ParsedAccount<LendingReserve>>())
|
||||||
|
}, [reserveAccounts]);
|
||||||
|
|
||||||
|
// TODO: subscribe to market updates
|
||||||
|
|
||||||
|
const enrichedObligations = useMemo(() => {
|
||||||
|
if (availableReserves.size === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return obligations
|
||||||
|
.map(obligation => (
|
||||||
|
{
|
||||||
|
obligation,
|
||||||
|
reserve: availableReserves.get(obligation.info.borrowReserve.toBase58()) as ParsedAccount<LendingReserve>,
|
||||||
|
collateralReserve: availableReserves.get(obligation.info.collateralReserve.toBase58()) as ParsedAccount<LendingReserve>
|
||||||
|
}
|
||||||
|
))
|
||||||
|
// use obligations with reserves available
|
||||||
|
.filter(item => item.reserve)
|
||||||
|
// use reserves with borrow amount greater than zero
|
||||||
|
.filter(item => wadToLamports(item.obligation.info.borrowAmountWad).toNumber() > 0)
|
||||||
|
.map(item => {
|
||||||
|
const obligation = item.obligation;
|
||||||
|
const reserve = item.reserve.info;
|
||||||
|
const liquidityMint = cache.get(reserve.liquidityMint) as ParsedAccount<MintInfo>;
|
||||||
|
let ltv = 0;
|
||||||
|
|
||||||
|
if(liquidityMint) {
|
||||||
|
const collateral = fromLamports(
|
||||||
|
collateralToLiquidity(obligation.info.depositedCollateral, item.reserve.info),
|
||||||
|
cache.get(item.collateralReserve.info.liquidityMint)?.info);
|
||||||
|
|
||||||
|
const borrowed = wadToLamports(obligation.info.borrowAmountWad).toNumber();
|
||||||
|
|
||||||
|
const borrowedAmount = simulateMarketOrderFill(
|
||||||
|
borrowed,
|
||||||
|
item.reserve.info,
|
||||||
|
item.reserve.info.dexMarketOption ? item.reserve.info.dexMarket : item.collateralReserve.info.dexMarket
|
||||||
|
);
|
||||||
|
|
||||||
|
ltv = 100 * collateral / borrowedAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const liquidationThreshold = item.reserve.info.config.liquidationThreshold;
|
||||||
|
const health = ltv / liquidationThreshold
|
||||||
|
return {
|
||||||
|
account: obligation,
|
||||||
|
info: {
|
||||||
|
...obligation.info,
|
||||||
|
ltv,
|
||||||
|
health,
|
||||||
|
}
|
||||||
|
} as EnrichedLendingObligation;
|
||||||
|
})
|
||||||
|
.sort((a, b) => a.info.health - b.info.health);
|
||||||
|
}, [obligations, availableReserves]);
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
obligations: enrichedObligations,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useEnrichedLendingObligation(address?: string | PublicKey) {
|
||||||
|
const id = typeof address === "string" ? address : address?.toBase58();
|
||||||
|
const { obligations } = useEnrichedLendingObligations();
|
||||||
|
|
||||||
|
const obligation = useMemo(() => {
|
||||||
|
return obligations.find(ob => ob.account.pubkey.toBase58() === id);
|
||||||
|
}, [obligations, id]);
|
||||||
|
|
||||||
|
return obligation;
|
||||||
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
import { useMemo } from "react";
|
|
||||||
import { useLendingObligations } from "./useLendingObligations";
|
|
||||||
import { collateralToLiquidity, LendingReserve } from "../models/lending";
|
|
||||||
import { useLendingReserves } from "./useLendingReserves";
|
|
||||||
import { ParsedAccount } from "../contexts/accounts";
|
|
||||||
import { wadToLamports } from "../utils/utils";
|
|
||||||
import { simulateMarketOrderFill } from "../contexts/market";
|
|
||||||
|
|
||||||
export const useLiquidableObligations = () => {
|
|
||||||
const { obligations } = useLendingObligations();
|
|
||||||
const { reserveAccounts } = useLendingReserves();
|
|
||||||
|
|
||||||
const availableReserves = useMemo(() => {
|
|
||||||
return reserveAccounts.reduce((map, reserve) => {
|
|
||||||
map.set(reserve.pubkey.toBase58(), reserve);
|
|
||||||
return map;
|
|
||||||
}, new Map<string, ParsedAccount<LendingReserve>>())
|
|
||||||
}, [reserveAccounts])
|
|
||||||
|
|
||||||
const liquidableObligations = useMemo(() => {
|
|
||||||
if (availableReserves.size === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return obligations
|
|
||||||
.map(obligation => (
|
|
||||||
{
|
|
||||||
obligation,
|
|
||||||
reserve: availableReserves.get(obligation.info.borrowReserve.toBase58()) as ParsedAccount<LendingReserve>
|
|
||||||
}
|
|
||||||
))
|
|
||||||
// use obligations with reserves available
|
|
||||||
.filter(item => item.reserve)
|
|
||||||
// use reserves with borrow amount greater than zero
|
|
||||||
.filter(item => wadToLamports(item.obligation.info.borrowAmountWad).toNumber() > 0)
|
|
||||||
.map(item => {
|
|
||||||
const obligation = item.obligation;
|
|
||||||
const reserve = item.reserve.info;
|
|
||||||
const collateralLamports = collateralToLiquidity(obligation.info.depositedCollateral, reserve);
|
|
||||||
const cost = simulateMarketOrderFill(collateralLamports, reserve);
|
|
||||||
|
|
||||||
console.log(cost);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useUserAccounts } from "./useUserAccounts";
|
import { useUserAccounts } from "./useUserAccounts";
|
||||||
import { useLendingObligations } from "./useLendingObligations";
|
import { useEnrichedLendingObligations } from "./useEnrichedLendingObligations";
|
||||||
import { TokenAccount } from "../models";
|
import { TokenAccount } from "../models";
|
||||||
|
|
||||||
export function useUserObligations() {
|
export function useUserObligations() {
|
||||||
const { userAccounts } = useUserAccounts();
|
const { userAccounts } = useUserAccounts();
|
||||||
const { obligations } = useLendingObligations();
|
const { obligations } = useEnrichedLendingObligations();
|
||||||
|
|
||||||
const accountsByMint = useMemo(() => {
|
const accountsByMint = useMemo(() => {
|
||||||
return userAccounts.reduce((res, acc) => {
|
return userAccounts.reduce((res, acc) => {
|
||||||
|
|
|
@ -188,11 +188,13 @@ export const collateralExchangeRate = (reserve?: LendingReserve) => {
|
||||||
return (reserve?.collateralMintSupply.toNumber() || 1) / reserveMarketCap(reserve);
|
return (reserve?.collateralMintSupply.toNumber() || 1) / reserveMarketCap(reserve);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const collateralToLiquidity = (collateralAmount: BN, reserve?: LendingReserve) => {
|
export const collateralToLiquidity = (collateralAmount: BN | number, reserve?: LendingReserve) => {
|
||||||
return Math.floor(collateralAmount.toNumber() / collateralExchangeRate(reserve));
|
const amount = typeof collateralAmount === 'number' ? collateralAmount : collateralAmount.toNumber();
|
||||||
|
return Math.floor(amount / collateralExchangeRate(reserve));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const liquidityToCollateral = (liquidityAmount: BN, reserve?: LendingReserve) => {
|
export const liquidityToCollateral = (liquidityAmount: BN | number, reserve?: LendingReserve) => {
|
||||||
return Math.floor(liquidityAmount.toNumber() * collateralExchangeRate(reserve));
|
const amount = typeof liquidityAmount === 'number' ? liquidityAmount : liquidityAmount.toNumber();
|
||||||
|
return Math.floor(amount * collateralExchangeRate(reserve));
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ export function toLamports(
|
||||||
typeof account === "number" ? account : account.info.amount?.toNumber();
|
typeof account === "number" ? account : account.info.amount?.toNumber();
|
||||||
|
|
||||||
const precision = Math.pow(10, mint?.decimals || 0);
|
const precision = Math.pow(10, mint?.decimals || 0);
|
||||||
return amount * precision;
|
return Math.floor(amount * precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wadToLamports(amount?: BN): BN {
|
export function wadToLamports(amount?: BN): BN {
|
||||||
|
@ -120,12 +120,12 @@ export function fromLamports(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount =
|
const amount = Math.floor(
|
||||||
typeof account === "number"
|
typeof account === "number"
|
||||||
? account
|
? account
|
||||||
: BN.isBN(account)
|
: BN.isBN(account)
|
||||||
? account.toNumber()
|
? account.toNumber()
|
||||||
: account.info.amount.toNumber();
|
: account.info.amount.toNumber());
|
||||||
|
|
||||||
const precision = Math.pow(10, mint?.decimals || 0);
|
const precision = Math.pow(10, mint?.decimals || 0);
|
||||||
return (amount / precision) * rate;
|
return (amount / precision) * rate;
|
||||||
|
|
|
@ -43,6 +43,7 @@ export const DashboardView = () => {
|
||||||
<div>{LABELS.TABLE_TITLE_YOUR_LOAN_BALANCE}</div>
|
<div>{LABELS.TABLE_TITLE_YOUR_LOAN_BALANCE}</div>
|
||||||
<div>{LABELS.TABLE_TITLE_COLLATERAL_BALANCE}</div>
|
<div>{LABELS.TABLE_TITLE_COLLATERAL_BALANCE}</div>
|
||||||
<div>{LABELS.TABLE_TITLE_APY}</div>
|
<div>{LABELS.TABLE_TITLE_APY}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_LTV}</div>
|
||||||
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
||||||
</div>
|
</div>
|
||||||
{userObligations.map((item) => {
|
{userObligations.map((item) => {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { useTokenName } from "../../hooks";
|
import { EnrichedLendingObligation, useTokenName } from "../../hooks";
|
||||||
import {
|
import {
|
||||||
calculateBorrowAPY,
|
calculateBorrowAPY,
|
||||||
collateralToLiquidity,
|
collateralToLiquidity,
|
||||||
LendingObligation,
|
|
||||||
LendingReserve,
|
LendingReserve,
|
||||||
} from "../../models/lending";
|
} from "../../models/lending";
|
||||||
import { TokenIcon } from "../../components/TokenIcon";
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
|
@ -18,7 +17,7 @@ import { Link } from "react-router-dom";
|
||||||
import { cache, ParsedAccount, useMint } from "../../contexts/accounts";
|
import { cache, ParsedAccount, useMint } from "../../contexts/accounts";
|
||||||
|
|
||||||
export const ObligationItem = (props: {
|
export const ObligationItem = (props: {
|
||||||
obligation: ParsedAccount<LendingObligation>;
|
obligation: EnrichedLendingObligation;
|
||||||
}) => {
|
}) => {
|
||||||
const { obligation } = props;
|
const { obligation } = props;
|
||||||
|
|
||||||
|
@ -51,17 +50,14 @@ export const ObligationItem = (props: {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className="dashboard-item">
|
<div className="dashboard-item">
|
||||||
<span style={{ display: "flex" }}>
|
<span style={{ display: "flex", marginLeft: 5 }}>
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }} title={`${collateralName}→${borrowName}`}>
|
||||||
<TokenIcon
|
<TokenIcon
|
||||||
mintAddress={collateralReserve?.info.liquidityMint}
|
mintAddress={collateralReserve?.info.liquidityMint}
|
||||||
style={{ marginRight: "-0.5rem" }}
|
style={{ marginRight: "-0.5rem" }}
|
||||||
/>
|
/>
|
||||||
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
||||||
</div>
|
</div>
|
||||||
{collateralName}
|
|
||||||
/
|
|
||||||
{borrowName}
|
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
{formatNumber.format(borrowAmount)} {borrowName}
|
{formatNumber.format(borrowAmount)} {borrowName}
|
||||||
|
@ -70,13 +66,14 @@ export const ObligationItem = (props: {
|
||||||
{formatNumber.format(collateral)} {collateralName}
|
{formatNumber.format(collateral)} {collateralName}
|
||||||
</div>
|
</div>
|
||||||
<div>{formatPct.format(borrowAPY)}</div>
|
<div>{formatPct.format(borrowAPY)}</div>
|
||||||
|
<div>{formatPct.format(obligation.info.ltv / 100)}</div>
|
||||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||||
<Link to={`/borrow/${borrowReserve.pubkey.toBase58()}`}>
|
<Link to={`/borrow/${borrowReserve.pubkey.toBase58()}`}>
|
||||||
<Button>
|
<Button>
|
||||||
<span>Borrow</span>
|
<span>Borrow</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={`/repay/loan/${obligation.pubkey.toBase58()}`}>
|
<Link to={`/repay/loan/${obligation.account.pubkey.toBase58()}`}>
|
||||||
<Button>
|
<Button>
|
||||||
<span>Repay</span>
|
<span>Repay</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
& > div, span {
|
& > div, span {
|
||||||
flex: 20%;
|
flex: 15%;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > :first-child {
|
& > :first-child {
|
||||||
flex: 120px
|
flex: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > :last-child {
|
& > :last-child {
|
||||||
|
@ -22,17 +22,17 @@
|
||||||
margin: 0px 30px;
|
margin: 0px 30px;
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
flex: 20%;
|
flex: 15%;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > :first-child {
|
& > :first-child {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
flex: 120px
|
flex: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > :last-child {
|
& > :last-child {
|
||||||
flex: 200px
|
flex: 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
import { LiquidateItem } from "./item";
|
import { LiquidateItem } from "./item";
|
||||||
import { useLiquidableObligations } from "./../../hooks";
|
import { useEnrichedLendingObligations } from "./../../hooks";
|
||||||
import "./style.less";
|
import "./style.less";
|
||||||
|
|
||||||
export const LiquidateView = () => {
|
export const LiquidateView = () => {
|
||||||
const { liquidableObligations } = useLiquidableObligations();
|
const { obligations } = useEnrichedLendingObligations();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="liquidate-container">
|
<div className="liquidate-container">
|
||||||
{liquidableObligations.length === 0 ? (
|
{obligations.length === 0 ? (
|
||||||
<div className="liquidate-info">{LABELS.LIQUIDATE_NO_LOANS}</div>
|
<div className="liquidate-info">{LABELS.LIQUIDATE_NO_LOANS}</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flexColumn">
|
<div className="flexColumn">
|
||||||
|
@ -20,8 +20,8 @@ export const LiquidateView = () => {
|
||||||
<div>{LABELS.TABLE_TITLE_LTV}</div>
|
<div>{LABELS.TABLE_TITLE_LTV}</div>
|
||||||
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
||||||
</div>
|
</div>
|
||||||
{liquidableObligations.map((item) => (
|
{obligations.map((item) => (
|
||||||
<LiquidateItem key={item.obligation.pubkey.toBase58()} obligation={item.obligation} ltv={item.ltv}></LiquidateItem>
|
<LiquidateItem key={item.account.pubkey.toBase58()} item={item}></LiquidateItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
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 { LendingReserve, calculateBorrowAPY } from "../../models/lending";
|
||||||
import { useTokenName } from "../../hooks";
|
import { EnrichedLendingObligation, useTokenName } from "../../hooks";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { Button, Card } from "antd";
|
import { Button, Card } from "antd";
|
||||||
import { TokenIcon } from "../../components/TokenIcon";
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
|
@ -14,25 +14,23 @@ import {
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
|
|
||||||
export const LiquidateItem = (props: {
|
export const LiquidateItem = (props: {
|
||||||
obligation: ParsedAccount<LendingObligation>;
|
item: EnrichedLendingObligation
|
||||||
ltv: number
|
|
||||||
}) => {
|
}) => {
|
||||||
|
let obligation = props.item.info
|
||||||
const { obligation, ltv } = props;
|
|
||||||
|
|
||||||
const borrowReserve = cache.get(
|
const borrowReserve = cache.get(
|
||||||
obligation.info.borrowReserve
|
obligation.borrowReserve
|
||||||
) as ParsedAccount<LendingReserve>;
|
) as ParsedAccount<LendingReserve>;
|
||||||
|
|
||||||
const collateralReserve = cache.get(
|
const collateralReserve = cache.get(
|
||||||
obligation.info.collateralReserve
|
obligation.collateralReserve
|
||||||
) as ParsedAccount<LendingReserve>;
|
) 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);
|
||||||
|
|
||||||
const borrowAmount = fromLamports(
|
const borrowAmount = fromLamports(
|
||||||
wadToLamports(obligation.info.borrowAmountWad),
|
wadToLamports(obligation.borrowAmountWad),
|
||||||
liquidityMint
|
liquidityMint
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -44,7 +42,7 @@ export const LiquidateItem = (props: {
|
||||||
const collateralName = useTokenName(collateralReserve?.info.liquidityMint);
|
const collateralName = useTokenName(collateralReserve?.info.liquidityMint);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/liquidate/${obligation.pubkey.toBase58()}`}>
|
<Link to={`/liquidate/${props.item.account.pubkey.toBase58()}`}>
|
||||||
<Card>
|
<Card>
|
||||||
<div className="liquidate-item">
|
<div className="liquidate-item">
|
||||||
<span style={{ display: "flex" }}>
|
<span style={{ display: "flex" }}>
|
||||||
|
@ -56,7 +54,7 @@ export const LiquidateItem = (props: {
|
||||||
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
||||||
</div>
|
</div>
|
||||||
{collateralName}
|
{collateralName}
|
||||||
/
|
→
|
||||||
{borrowName}
|
{borrowName}
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
|
@ -66,7 +64,7 @@ export const LiquidateItem = (props: {
|
||||||
{formatPct.format(borrowAPY)}
|
{formatPct.format(borrowAPY)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{formatPct.format(ltv / 100)}
|
{formatPct.format(obligation.ltv / 100)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button>
|
<Button>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useLendingObligation, useLendingReserve } from "../../hooks";
|
import { useEnrichedLendingObligation, useLendingReserve } from "../../hooks";
|
||||||
import {
|
import {
|
||||||
SideReserveOverview,
|
SideReserveOverview,
|
||||||
SideReserveOverviewMode,
|
SideReserveOverviewMode,
|
||||||
|
@ -13,7 +13,7 @@ import "./style.less";
|
||||||
export const LiquidateReserveView = () => {
|
export const LiquidateReserveView = () => {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
const obligation = useLendingObligation(id);
|
const obligation = useEnrichedLendingObligation(id);
|
||||||
const reserve = useLendingReserve(obligation?.info.borrowReserve);
|
const reserve = useLendingReserve(obligation?.info.borrowReserve);
|
||||||
const collateralReserve = useLendingReserve(obligation?.info.collateralReserve);
|
const collateralReserve = useLendingReserve(obligation?.info.collateralReserve);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useLendingObligation, useLendingReserve } from "../../hooks";
|
import { useEnrichedLendingObligation, useLendingReserve } from "../../hooks";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
import { RepayInput } from "../../components/RepayInput";
|
import { RepayInput } from "../../components/RepayInput";
|
||||||
|
@ -16,7 +16,7 @@ export const RepayReserveView = () => {
|
||||||
obligation?: string;
|
obligation?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const lendingObligation = useLendingObligation(obligationId);
|
const lendingObligation = useEnrichedLendingObligation(obligationId);
|
||||||
const lendingReserve = useLendingReserve(
|
const lendingReserve = useLendingReserve(
|
||||||
obligationId ? lendingObligation?.info.borrowReserve : reserveId
|
obligationId ? lendingObligation?.info.borrowReserve : reserveId
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue