feat: liquidations

This commit is contained in:
bartosz-lipinski 2020-12-15 22:22:25 -06:00
parent cc9e092835
commit 390b77cde7
7 changed files with 64 additions and 45 deletions

View File

@ -6,7 +6,7 @@ import {
GithubOutlined,
BankOutlined,
LogoutOutlined,
LoginOutlined,
ShoppingOutlined,
HomeOutlined,
RocketOutlined,
} from "@ant-design/icons";
@ -94,7 +94,7 @@ export const AppLayout = (props: any) => {
{LABELS.MENU_BORROW}
</Link>
</Menu.Item>
<Menu.Item key="5" icon={<LoginOutlined />}>
<Menu.Item key="5" icon={<ShoppingOutlined />}>
<Link
to={{
pathname: "/liquidate",

View File

@ -12,12 +12,11 @@ import "./style.less";
export const LiquidateInput = (props: {
className?: string;
reserve: ParsedAccount<LendingReserve>;
collateralReserve?: ParsedAccount<LendingReserve>;
obligation: ParsedAccount<LendingObligation>;
}) => {
const { reserve } = props;
const [collateralReserveMint, setCollateralReserveMint] = useState<string>();
const { reserve, collateralReserve } = props;
const [pendingTx, setPendingTx] = useState(false);
const onLiquidate = useCallback(() => {
@ -43,8 +42,8 @@ export const LiquidateInput = (props: {
<div className="liquidate-input-title">{LABELS.SELECT_COLLATERAL}</div>
<CollateralSelector
reserve={reserve.info}
mint={collateralReserveMint}
onMintChange={setCollateralReserveMint}
collateralReserve={collateralReserve?.pubkey.toBase58()}
disabled={true}
/>
<Button
type="primary"

View File

@ -2,14 +2,14 @@ import React, { useCallback, useContext, useEffect, useState } from "react";
import { MINT_TO_MARKET } from "./../models/marketOverrides";
import { STABLE_COINS } from "./../utils/utils";
import { useConnectionConfig } from "./connection";
import { cache, getMultipleAccounts } from "./accounts";
import { cache, getMultipleAccounts, ParsedAccount } from "./accounts";
import { Market, MARKETS, Orderbook, TOKEN_MINTS } from "@project-serum/serum";
import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
import { useMemo } from "react";
import { EventEmitter } from "./../utils/eventEmitter";
import { DexMarketParser } from "./../models/dex";
import { LendingReserve } from "../models";
import { LendingMarket, LendingReserve } from "../models";
export interface MarketsContextState {
midPriceInUSD: (mint: string) => number;
@ -261,7 +261,11 @@ export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve)
const quoteMintDecimals =
cache.get(decodedMarket.quoteMint)?.info.decimals || 0;
const market = new Market(
const lendingMarket = cache.get(reserve.lendingMarket) as ParsedAccount<
LendingMarket
>;
const dexMarket = new Market(
decodedMarket,
baseMintDecimals,
quoteMintDecimals,
@ -269,19 +273,23 @@ export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve)
decodedMarket.programId
);
const bids = cache.get(decodedMarket.bids)?.info;
const asks = cache.get(decodedMarket.asks)?.info;
const bookAccount = lendingMarket.info.quoteMint.equals(
reserve.liquidityMint
)
? decodedMarket?.bids
: decodedMarket?.asks;
if (bids && asks) {
const bidsBook = new Orderbook(market, bids.accountFlags, bids.slab);
const asksBook = new Orderbook(market, asks.accountFlags, asks.slab);
const bookInfo = cache.get(bookAccount)?.info;
if (!bookInfo) {
return 0;
}
// TODO: pick side
let book = bidsBook;
const book = new Orderbook(dexMarket, bookInfo.accountFlags, bookInfo.slab);
let cost = 0;
let remaining = amount;
if (book) {
for (const level of book) {
let size = remaining > level.size ? level.size : level.size - remaining;
cost = cost + level.price * size;
@ -291,11 +299,9 @@ export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve)
break;
}
}
return cost;
}
return 0;
return cost;
}
const getMidPrice = (marketAddress?: string, mintAddress?: string) => {

View File

@ -1,8 +1,10 @@
import { useMemo } from "react";
import { useLendingObligations } from "./useLendingObligations";
import { LendingReserve } from "../models/lending";
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();
@ -27,8 +29,16 @@ export const useLiquidableObligations = () => {
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);
// TODO: calculate LTV
const ltv = 81;
const liquidationThreshold = item.reserve.info.config.liquidationThreshold;

View File

@ -51,22 +51,6 @@ export const ObligationItem = (props: {
const borrowName = useTokenName(borrowReserve?.info.liquidityMint);
const collateralName = useTokenName(collateralReserve?.info.liquidityMint);
const cost = simulateMarketOrderFill(collateralLamports, borrowReserve.info);
console.log(cost);
// TODO: health factor
// let borrow_amount_as_collateral = withdraw_reserve_rates.liquidity_to_collateral(
// simulate_market_order_fill(
// obligation.borrowed_liquidity_wads,
// memory,
// dex_market_order_book_side_info,
// dex_market_info,
// &repay_reserve,
// )?
// .round_u64(),
// );
return (
<Card>
<div className="dashboard-item">

View File

@ -20,7 +20,14 @@ export const LiquidateItem = (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 collateralReserve = cache.get(
obligation.info.collateralReserve
) as ParsedAccount<LendingReserve>;
const tokenName = useTokenName(borrowReserve?.info.liquidityMint);
const liquidityMint = useMint(borrowReserve.info.liquidityMint);
@ -33,13 +40,24 @@ export const LiquidateItem = (props: {
borrowReserve,
]);
const borrowName = useTokenName(borrowReserve?.info.liquidityMint);
const collateralName = useTokenName(collateralReserve?.info.liquidityMint);
return (
<Link to={`/liquidate/${obligation.pubkey.toBase58()}`}>
<Card>
<div className="liquidate-item">
<span style={{ display: "flex" }}>
<TokenIcon mintAddress={borrowReserve.info.liquidityMint} />
{tokenName}
<div style={{ display: "flex" }}>
<TokenIcon
mintAddress={collateralReserve?.info.liquidityMint}
style={{ marginRight: "-0.5rem" }}
/>
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
</div>
{collateralName}
/
{borrowName}
</span>
<div>
{formatNumber.format(borrowAmount)} {tokenName}

View File

@ -15,6 +15,7 @@ export const LiquidateReserveView = () => {
const obligation = useLendingObligation(id);
const reserve = useLendingReserve(obligation?.info.borrowReserve);
const collateralReserve = useLendingReserve(obligation?.info.collateralReserve);
if (!obligation || !reserve) {
return null;
@ -26,6 +27,7 @@ export const LiquidateReserveView = () => {
<LiquidateInput
className="liquidate-reserve-item liquidate-reserve-item-left"
obligation={obligation}
collateralReserve={collateralReserve}
reserve={reserve}
/>
<SideReserveOverview