feat: liquidations
This commit is contained in:
parent
cc9e092835
commit
390b77cde7
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
@ -90,7 +90,7 @@ export function MarketProvider({ children = null as any }) {
|
|||
allMarkets.filter((a) => cache.get(a) === undefined),
|
||||
"single"
|
||||
).then(({ keys, array }) => {
|
||||
allMarkets.forEach(() => {});
|
||||
allMarkets.forEach(() => { });
|
||||
|
||||
return array.map((item, index) => {
|
||||
const marketAddress = keys[index];
|
||||
|
@ -159,7 +159,7 @@ export function MarketProvider({ children = null as any }) {
|
|||
const info = marketByMint.get(mintAddress);
|
||||
const market = cache.get(info?.marketInfo.address.toBase58() || "");
|
||||
if (!market) {
|
||||
return () => {};
|
||||
return () => { };
|
||||
}
|
||||
|
||||
// TODO: get recent volume
|
||||
|
@ -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,33 +273,35 @@ 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;
|
||||
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;
|
||||
remaining = remaining - size;
|
||||
|
||||
if(remaining <= 0) {
|
||||
if (remaining <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return cost;
|
||||
}
|
||||
|
||||
const getMidPrice = (marketAddress?: string, mintAddress?: string) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue