mirror of https://github.com/certusone/oyster.git
style: format
This commit is contained in:
parent
306cff16c7
commit
179e4352bc
|
@ -104,7 +104,7 @@ export const repay = async (
|
|||
authority
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
let tx = await sendTransaction(
|
||||
connection,
|
||||
wallet,
|
||||
|
|
|
@ -52,7 +52,7 @@ export const BorrowInput = (props: {
|
|||
|
||||
const { userObligationsByReserve } = useUserObligationByReserve(
|
||||
borrowReserve?.pubkey,
|
||||
collateralReserve?.pubkey,
|
||||
collateralReserve?.pubkey
|
||||
);
|
||||
|
||||
const onBorrow = useCallback(() => {
|
||||
|
|
|
@ -29,7 +29,7 @@ export const CollateralSelector = (props: {
|
|||
<Select
|
||||
size="large"
|
||||
showSearch
|
||||
style={{ minWidth: 300 , margin: "5px 0px" }}
|
||||
style={{ minWidth: 300, margin: "5px 0px" }}
|
||||
placeholder="Collateral"
|
||||
value={props.collateralReserve}
|
||||
disabled={props.disabled}
|
||||
|
|
|
@ -17,7 +17,9 @@ export const DepositInfoLine = (props: {
|
|||
}) => {
|
||||
const name = useTokenName(props.reserve.liquidityMint);
|
||||
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||
const { balance: collateralBalance } = useUserCollateralBalance(props.reserve);
|
||||
const { balance: collateralBalance } = useUserCollateralBalance(
|
||||
props.reserve
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
|
|
|
@ -106,9 +106,7 @@ export const DepositInput = (props: {
|
|||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<div className="deposit-input-title">
|
||||
{LABELS.DEPOSIT_QUESTION}
|
||||
</div>
|
||||
<div className="deposit-input-title">{LABELS.DEPOSIT_QUESTION}</div>
|
||||
<div className="token-input">
|
||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||
<NumericInput
|
||||
|
|
|
@ -16,7 +16,6 @@ export const LiquidateInput = (props: {
|
|||
collateralReserve?: ParsedAccount<LendingReserve>;
|
||||
obligation: EnrichedLendingObligation;
|
||||
}) => {
|
||||
|
||||
const { reserve, collateralReserve } = props;
|
||||
const [pendingTx, setPendingTx] = useState(false);
|
||||
|
||||
|
@ -34,27 +33,25 @@ export const LiquidateInput = (props: {
|
|||
};
|
||||
|
||||
return (
|
||||
<Card className={props.className} bodyStyle={bodyStyle} >
|
||||
<div style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}>
|
||||
<Card className={props.className} bodyStyle={bodyStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<div className="liquidate-input-title">{LABELS.SELECT_COLLATERAL}</div>
|
||||
<CollateralSelector
|
||||
reserve={reserve.info}
|
||||
collateralReserve={collateralReserve?.pubkey.toBase58()}
|
||||
disabled={true}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onLiquidate}
|
||||
loading={pendingTx}
|
||||
>
|
||||
<Button type="primary" onClick={onLiquidate} loading={pendingTx}>
|
||||
{LABELS.LIQUIDATE_ACTION}
|
||||
</Button>
|
||||
<BackButton />
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import React, { useCallback, useState } from "react";
|
||||
import { EnrichedLendingObligation, InputType, useAccountByMint, useSliderInput, useTokenName, useUserBalance } from "../../hooks";
|
||||
import {
|
||||
LendingReserve,
|
||||
} from "../../models";
|
||||
EnrichedLendingObligation,
|
||||
InputType,
|
||||
useAccountByMint,
|
||||
useSliderInput,
|
||||
useTokenName,
|
||||
useUserBalance,
|
||||
} from "../../hooks";
|
||||
import { LendingReserve } from "../../models";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { Button, Card, Slider } from "antd";
|
||||
import { ParsedAccount, useMint } from "../../contexts/accounts";
|
||||
|
@ -33,11 +38,10 @@ export const RepayInput = (props: {
|
|||
|
||||
const liquidityMint = useMint(repayReserve.info.liquidityMint);
|
||||
|
||||
const borrowAmountLamports = wadToLamports(obligation.info.borrowAmountWad).toNumber();
|
||||
const borrowAmount = fromLamports(
|
||||
borrowAmountLamports,
|
||||
liquidityMint
|
||||
);
|
||||
const borrowAmountLamports = wadToLamports(
|
||||
obligation.info.borrowAmountWad
|
||||
).toNumber();
|
||||
const borrowAmount = fromLamports(borrowAmountLamports, liquidityMint);
|
||||
const collateralReserve = props.collateralReserve;
|
||||
|
||||
const name = useTokenName(repayReserve?.info.liquidityMint);
|
||||
|
@ -74,9 +78,12 @@ export const RepayInput = (props: {
|
|||
|
||||
(async () => {
|
||||
try {
|
||||
const toRepayLamports = type === InputType.Percent
|
||||
? (pct * borrowAmountLamports) / 100
|
||||
: Math.ceil(borrowAmountLamports * (parseFloat(value) / borrowAmount));
|
||||
const toRepayLamports =
|
||||
type === InputType.Percent
|
||||
? (pct * borrowAmountLamports) / 100
|
||||
: Math.ceil(
|
||||
borrowAmountLamports * (parseFloat(value) / borrowAmount)
|
||||
);
|
||||
|
||||
await repay(
|
||||
fromAccounts[0],
|
||||
|
@ -97,7 +104,6 @@ export const RepayInput = (props: {
|
|||
type: "error",
|
||||
description: error.message,
|
||||
});
|
||||
|
||||
} finally {
|
||||
setPendingTx(false);
|
||||
}
|
||||
|
@ -138,9 +144,7 @@ export const RepayInput = (props: {
|
|||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<div className="repay-input-title">
|
||||
{LABELS.REPAY_QUESTION}
|
||||
</div>
|
||||
<div className="repay-input-title">{LABELS.REPAY_QUESTION}</div>
|
||||
<div className="token-input">
|
||||
<TokenIcon mintAddress={repayReserve?.info.liquidityMint} />
|
||||
<NumericInput
|
||||
|
@ -157,11 +161,7 @@ export const RepayInput = (props: {
|
|||
/>
|
||||
<div>{name}</div>
|
||||
</div>
|
||||
<Slider
|
||||
marks={marks}
|
||||
value={pct}
|
||||
onChange={setPct}
|
||||
/>
|
||||
<Slider marks={marks} value={pct} onChange={setPct} />
|
||||
<div className="repay-input-title">{LABELS.COLLATERAL}</div>
|
||||
<CollateralSelector
|
||||
reserve={repayReserve.info}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import {
|
||||
fromLamports,
|
||||
wadToLamports,
|
||||
} from "../../utils/utils";
|
||||
import { fromLamports, wadToLamports } from "../../utils/utils";
|
||||
import { useMint } from "../../contexts/accounts";
|
||||
import { WaterWave } from "./../WaterWave";
|
||||
|
||||
|
@ -23,7 +20,10 @@ export const ReserveUtilizationChart = (props: { reserve: LendingReserve }) => {
|
|||
[props.reserve, liquidityMint]
|
||||
);
|
||||
|
||||
return <WaterWave
|
||||
style={{ height: 300 }}
|
||||
percent={availableLiquidity * 100 / (availableLiquidity + totalBorrows)} />;
|
||||
return (
|
||||
<WaterWave
|
||||
style={{ height: 300 }}
|
||||
percent={(availableLiquidity * 100) / (availableLiquidity + totalBorrows)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -24,7 +24,9 @@ export const UserLendingCard = (props: {
|
|||
const name = useTokenName(reserve?.liquidityMint);
|
||||
|
||||
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||
const { balance: collateralBalance } = useUserCollateralBalance(props.reserve);
|
||||
const { balance: collateralBalance } = useUserCollateralBalance(
|
||||
props.reserve
|
||||
);
|
||||
|
||||
const { borrowed: totalBorrowed } = useBorrowedAmount(address);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import "./index.less";
|
||||
|
||||
export const WaterWave = (props: any) => {
|
||||
|
@ -17,9 +17,9 @@ export const WaterWave = (props: any) => {
|
|||
useEffect(() => {
|
||||
resize();
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
window.addEventListener("resize", resize);
|
||||
return () => {
|
||||
window.removeEventListener('resize', resize);
|
||||
window.removeEventListener("resize", resize);
|
||||
};
|
||||
}, [resize]);
|
||||
|
||||
|
@ -31,11 +31,12 @@ export const WaterWave = (props: any) => {
|
|||
(val) => {
|
||||
timer = val;
|
||||
},
|
||||
color);
|
||||
color
|
||||
);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(timer);
|
||||
}
|
||||
};
|
||||
}, [percent, color]);
|
||||
|
||||
return (
|
||||
|
@ -44,7 +45,7 @@ export const WaterWave = (props: any) => {
|
|||
ref={root as any}
|
||||
style={{ transform: `scale(${radio})` }}
|
||||
>
|
||||
<div style={{ width: height, height, overflow: 'hidden' }}>
|
||||
<div style={{ width: height, height, overflow: "hidden" }}>
|
||||
<canvas
|
||||
className="waterWaveCanvasWrapper"
|
||||
ref={node as any}
|
||||
|
@ -58,16 +59,20 @@ export const WaterWave = (props: any) => {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const renderChart = (canvas: HTMLCanvasElement | undefined, percent: number, setTimer: (timer: number) => void, color = '#1890FF',) => {
|
||||
const renderChart = (
|
||||
canvas: HTMLCanvasElement | undefined,
|
||||
percent: number,
|
||||
setTimer: (timer: number) => void,
|
||||
color = "#1890FF"
|
||||
) => {
|
||||
const data = percent / 100;
|
||||
if (!canvas || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
if (!ctx) {
|
||||
return;
|
||||
|
@ -95,7 +100,10 @@ const renderChart = (canvas: HTMLCanvasElement | undefined, percent: number, set
|
|||
const circleOffset = -(Math.PI / 2);
|
||||
let circleLock = true;
|
||||
|
||||
const cStartPoint = [radius + bR * Math.cos(circleOffset), radius + bR * Math.sin(circleOffset)];
|
||||
const cStartPoint = [
|
||||
radius + bR * Math.cos(circleOffset),
|
||||
radius + bR * Math.sin(circleOffset),
|
||||
];
|
||||
ctx.strokeStyle = color;
|
||||
ctx.moveTo(cStartPoint[0], cStartPoint[1]);
|
||||
|
||||
|
@ -125,19 +133,19 @@ const renderChart = (canvas: HTMLCanvasElement | undefined, percent: number, set
|
|||
ctx.lineTo(startPoint[0], startPoint[1]);
|
||||
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);
|
||||
gradient.addColorStop(0, '#ffffff');
|
||||
gradient.addColorStop(1, '#1890FF');
|
||||
gradient.addColorStop(0, "#ffffff");
|
||||
gradient.addColorStop(1, "#1890FF");
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
if (circleLock) {
|
||||
circleLock = false;
|
||||
|
||||
ctx.globalCompositeOperation = 'destination-over';
|
||||
ctx.globalCompositeOperation = "destination-over";
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.save();
|
||||
|
@ -145,7 +153,7 @@ const renderChart = (canvas: HTMLCanvasElement | undefined, percent: number, set
|
|||
ctx.stroke();
|
||||
ctx.restore();
|
||||
ctx.clip();
|
||||
ctx.fillStyle = '#1890FF';
|
||||
ctx.fillStyle = "#1890FF";
|
||||
} else {
|
||||
if (data >= 0.85) {
|
||||
if (currRange > range / 4) {
|
||||
|
@ -178,7 +186,7 @@ const renderChart = (canvas: HTMLCanvasElement | undefined, percent: number, set
|
|||
drawSin();
|
||||
}
|
||||
setTimer(requestAnimationFrame(render));
|
||||
}
|
||||
};
|
||||
|
||||
render();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
@ -249,7 +249,11 @@ export const usePrecacheMarket = () => {
|
|||
return context.precacheMarkets;
|
||||
};
|
||||
|
||||
export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve, dex: PublicKey) => {
|
||||
export const simulateMarketOrderFill = (
|
||||
amount: number,
|
||||
reserve: LendingReserve,
|
||||
dex: PublicKey
|
||||
) => {
|
||||
const liquidityMint = cache.get(reserve.liquidityMint);
|
||||
const collateralMint = cache.get(reserve.collateralMint);
|
||||
if (!liquidityMint || !collateralMint) {
|
||||
|
@ -279,9 +283,7 @@ export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve,
|
|||
decodedMarket.programId
|
||||
);
|
||||
|
||||
const bookAccount = lendingMarket.info.quoteMint.equals(
|
||||
reserve.liquidityMint
|
||||
)
|
||||
const bookAccount = lendingMarket.info.quoteMint.equals(reserve.liquidityMint)
|
||||
? decodedMarket?.bids
|
||||
: decodedMarket?.asks;
|
||||
|
||||
|
@ -299,9 +301,9 @@ export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve,
|
|||
const depth = book.getL2(1000);
|
||||
let price, sizeAtLevel: number;
|
||||
|
||||
const op = book.isBids ?
|
||||
(price: number, size: number) => size / price :
|
||||
(price: number, size: number) => size * price;
|
||||
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;
|
||||
|
@ -315,7 +317,7 @@ export const simulateMarketOrderFill = (amount: number, reserve: LendingReserve,
|
|||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
};
|
||||
|
||||
const getMidPrice = (marketAddress?: string, mintAddress?: string) => {
|
||||
const SERUM_TOKEN = TOKEN_MINTS.find(
|
||||
|
|
|
@ -2,7 +2,11 @@ import { PublicKey } from "@solana/web3.js";
|
|||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { cache, ParsedAccount } from "./../contexts/accounts";
|
||||
import { useLendingObligations } from "./useLendingObligations";
|
||||
import { collateralToLiquidity, LendingObligation, LendingReserve } from "../models/lending";
|
||||
import {
|
||||
collateralToLiquidity,
|
||||
LendingObligation,
|
||||
LendingReserve,
|
||||
} from "../models/lending";
|
||||
import { useLendingReserves } from "./useLendingReserves";
|
||||
import { fromLamports, wadToLamports } from "../utils/utils";
|
||||
import { MintInfo } from "@solana/spl-token";
|
||||
|
@ -10,7 +14,7 @@ import { simulateMarketOrderFill, useMarkets } from "../contexts/market";
|
|||
|
||||
interface EnrichedLendingObligationInfo extends LendingObligation {
|
||||
ltv: number;
|
||||
health: number
|
||||
health: number;
|
||||
}
|
||||
|
||||
export interface EnrichedLendingObligation {
|
||||
|
@ -25,67 +29,86 @@ export function useEnrichedLendingObligations() {
|
|||
|
||||
const availableReserves = useMemo(() => {
|
||||
return reserveAccounts.reduce((map, reserve) => {
|
||||
map.set(reserve.pubkey.toBase58(), reserve);
|
||||
map.set(reserve.pubkey.toBase58(), reserve);
|
||||
return map;
|
||||
}, new Map<string, ParsedAccount<LendingReserve>>())
|
||||
}, new Map<string, ParsedAccount<LendingReserve>>());
|
||||
}, [reserveAccounts]);
|
||||
|
||||
|
||||
const enrichedFactory = useCallback(() => {
|
||||
if (availableReserves.size === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return obligations
|
||||
.map(obligation => (
|
||||
{
|
||||
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;
|
||||
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);
|
||||
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 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
|
||||
);
|
||||
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,
|
||||
// TODO: add borrow and collateral expressed in lending market quote ccy
|
||||
ltv = (100 * collateral) / borrowedAmount;
|
||||
}
|
||||
} as EnrichedLendingObligation;
|
||||
})
|
||||
.sort((a, b) => a.info.health - b.info.health);
|
||||
|
||||
const liquidationThreshold =
|
||||
item.reserve.info.config.liquidationThreshold;
|
||||
const health = ltv / liquidationThreshold;
|
||||
return {
|
||||
account: obligation,
|
||||
info: {
|
||||
...obligation.info,
|
||||
ltv,
|
||||
health,
|
||||
// TODO: add borrow and collateral expressed in lending market quote ccy
|
||||
},
|
||||
} as EnrichedLendingObligation;
|
||||
})
|
||||
.sort((a, b) => a.info.health - b.info.health)
|
||||
);
|
||||
}, [obligations, availableReserves]);
|
||||
|
||||
const [enriched, setEnriched] = useState<EnrichedLendingObligation[]>(enrichedFactory());
|
||||
const [enriched, setEnriched] = useState<EnrichedLendingObligation[]>(
|
||||
enrichedFactory()
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const dispose = marketEmitter.onMarket(() => {
|
||||
|
@ -107,7 +130,7 @@ export function useEnrichedLendingObligation(address?: string | PublicKey) {
|
|||
const { obligations } = useEnrichedLendingObligations();
|
||||
|
||||
const obligation = useMemo(() => {
|
||||
return obligations.find(ob => ob.account.pubkey.toBase58() === id);
|
||||
return obligations.find((ob) => ob.account.pubkey.toBase58() === id);
|
||||
}, [obligations, id]);
|
||||
|
||||
return obligation;
|
||||
|
|
|
@ -32,7 +32,9 @@ export function useLendingObligations() {
|
|||
|
||||
export function useLendingObligation(address?: string | PublicKey) {
|
||||
const id = typeof address === "string" ? address : address?.toBase58();
|
||||
const [obligationAccount, setObligationAccount] = useState(cache.get(id || "") as ParsedAccount<LendingObligation>);
|
||||
const [obligationAccount, setObligationAccount] = useState(
|
||||
cache.get(id || "") as ParsedAccount<LendingObligation>
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const dispose = cache.emitter.onCache((args) => {
|
||||
|
|
|
@ -3,15 +3,23 @@ import { useUserObligations } from "./useUserObligations";
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export function useUserObligationByReserve(
|
||||
borrowReserve?: string | PublicKey,
|
||||
collateralReserve?: string | PublicKey) {
|
||||
borrowReserve?: string | PublicKey,
|
||||
collateralReserve?: string | PublicKey
|
||||
) {
|
||||
const { userObligations } = useUserObligations();
|
||||
|
||||
const userObligationsByReserve = useMemo(() => {
|
||||
const borrowId = typeof borrowReserve === "string" ? borrowReserve : borrowReserve?.toBase58();
|
||||
const collateralId = typeof collateralReserve === "string" ? collateralReserve : collateralReserve?.toBase58();
|
||||
const borrowId =
|
||||
typeof borrowReserve === "string"
|
||||
? borrowReserve
|
||||
: borrowReserve?.toBase58();
|
||||
const collateralId =
|
||||
typeof collateralReserve === "string"
|
||||
? collateralReserve
|
||||
: collateralReserve?.toBase58();
|
||||
return userObligations.filter(
|
||||
(item) => item.obligation.info.borrowReserve.toBase58() === borrowId &&
|
||||
(item) =>
|
||||
item.obligation.info.borrowReserve.toBase58() === borrowId &&
|
||||
item.obligation.info.collateralReserve.toBase58() === collateralId
|
||||
);
|
||||
}, [borrowReserve, collateralReserve, userObligations]);
|
||||
|
|
|
@ -41,7 +41,7 @@ export const LendingObligationParser = (
|
|||
info: AccountInfo<Buffer>
|
||||
) => {
|
||||
const buffer = Buffer.from(info.data);
|
||||
const data = LendingObligationLayout.decode(buffer);
|
||||
const data = LendingObligationLayout.decode(buffer);
|
||||
|
||||
const details = {
|
||||
pubkey: pubKey,
|
||||
|
|
|
@ -183,16 +183,29 @@ export const reserveMarketCap = (reserve?: LendingReserve) => {
|
|||
};
|
||||
|
||||
export const collateralExchangeRate = (reserve?: LendingReserve) => {
|
||||
return (reserve?.collateralMintSupply.toNumber() || 1) / reserveMarketCap(reserve);
|
||||
}
|
||||
return (
|
||||
(reserve?.collateralMintSupply.toNumber() || 1) / reserveMarketCap(reserve)
|
||||
);
|
||||
};
|
||||
|
||||
export const collateralToLiquidity = (collateralAmount: BN | number, reserve?: LendingReserve) => {
|
||||
const amount = typeof collateralAmount === 'number' ? collateralAmount : collateralAmount.toNumber();
|
||||
export const collateralToLiquidity = (
|
||||
collateralAmount: BN | number,
|
||||
reserve?: LendingReserve
|
||||
) => {
|
||||
const amount =
|
||||
typeof collateralAmount === "number"
|
||||
? collateralAmount
|
||||
: collateralAmount.toNumber();
|
||||
return Math.floor(amount / collateralExchangeRate(reserve));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const liquidityToCollateral = (liquidityAmount: BN | number, reserve?: LendingReserve) => {
|
||||
const amount = typeof liquidityAmount === 'number' ? liquidityAmount : liquidityAmount.toNumber();
|
||||
export const liquidityToCollateral = (
|
||||
liquidityAmount: BN | number,
|
||||
reserve?: LendingReserve
|
||||
) => {
|
||||
const amount =
|
||||
typeof liquidityAmount === "number"
|
||||
? liquidityAmount
|
||||
: liquidityAmount.toNumber();
|
||||
return Math.floor(amount * collateralExchangeRate(reserve));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
ReserveView,
|
||||
WithdrawView,
|
||||
LiquidateView,
|
||||
LiquidateReserveView
|
||||
LiquidateReserveView,
|
||||
} from "./views";
|
||||
|
||||
export function Routes() {
|
||||
|
|
|
@ -125,7 +125,8 @@ export function fromLamports(
|
|||
? account
|
||||
: BN.isBN(account)
|
||||
? account.toNumber()
|
||||
: account.info.amount.toNumber());
|
||||
: account.info.amount.toNumber()
|
||||
);
|
||||
|
||||
const precision = Math.pow(10, mint?.decimals || 0);
|
||||
return (amount / precision) * rate;
|
||||
|
|
|
@ -17,7 +17,9 @@ export const BorrowItem = (props: {
|
|||
const price = useMidPriceInUSD(props.reserve.liquidityMint.toBase58()).price;
|
||||
|
||||
// TODO: calculate avilable amount... based on total owned collateral across all the reserves
|
||||
const { balance: collateralBalance } = useUserCollateralBalance(props.reserve);
|
||||
const { balance: collateralBalance } = useUserCollateralBalance(
|
||||
props.reserve
|
||||
);
|
||||
|
||||
const apr = calculateBorrowAPY(props.reserve);
|
||||
|
||||
|
|
|
@ -40,8 +40,11 @@ export const ObligationItem = (props: {
|
|||
const borrowAPY = useMemo(() => calculateBorrowAPY(borrowReserve.info), [
|
||||
borrowReserve,
|
||||
]);
|
||||
|
||||
const collateralLamports = collateralToLiquidity(obligation.info.depositedCollateral, borrowReserve.info);
|
||||
|
||||
const collateralLamports = collateralToLiquidity(
|
||||
obligation.info.depositedCollateral,
|
||||
borrowReserve.info
|
||||
);
|
||||
const collateral = fromLamports(collateralLamports, collateralMint);
|
||||
|
||||
const borrowName = useTokenName(borrowReserve?.info.liquidityMint);
|
||||
|
@ -51,7 +54,10 @@ export const ObligationItem = (props: {
|
|||
<Card>
|
||||
<div className="dashboard-item">
|
||||
<span style={{ display: "flex", marginLeft: 5 }}>
|
||||
<div style={{ display: "flex" }} title={`${collateralName}→${borrowName}`}>
|
||||
<div
|
||||
style={{ display: "flex" }}
|
||||
title={`${collateralName}→${borrowName}`}
|
||||
>
|
||||
<TokenIcon
|
||||
mintAddress={collateralReserve?.info.liquidityMint}
|
||||
style={{ marginRight: "-0.5rem" }}
|
||||
|
|
|
@ -18,7 +18,9 @@ export const ReserveItem = (props: {
|
|||
}) => {
|
||||
const name = useTokenName(props.reserve.liquidityMint);
|
||||
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||
const { balance: collateralBalance } = useUserCollateralBalance(props.reserve);
|
||||
const { balance: collateralBalance } = useUserCollateralBalance(
|
||||
props.reserve
|
||||
);
|
||||
|
||||
const apy = calculateDepositAPY(props.reserve);
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ export const HomeView = () => {
|
|||
reserveAccounts.forEach((item) => {
|
||||
const marketCapLamports = reserveMarketCap(item.info);
|
||||
|
||||
|
||||
const localCache = cache;
|
||||
const liquidityMint = localCache.get(
|
||||
item.info.liquidityMint.toBase58()
|
||||
|
@ -40,9 +39,12 @@ export const HomeView = () => {
|
|||
|
||||
totalSize = totalSize + marketCap;
|
||||
|
||||
borrowed = borrowed + fromLamports(
|
||||
wadToLamports(item.info?.borrowedLiquidityWad).toNumber(),
|
||||
liquidityMint.info);
|
||||
borrowed =
|
||||
borrowed +
|
||||
fromLamports(
|
||||
wadToLamports(item.info?.borrowedLiquidityWad).toNumber(),
|
||||
liquidityMint.info
|
||||
);
|
||||
});
|
||||
|
||||
setTotalMarketSize(totalSize);
|
||||
|
@ -64,18 +66,18 @@ export const HomeView = () => {
|
|||
<div className="flexColumn">
|
||||
<Row gutter={16} className="home-info-row">
|
||||
<Col span={12}>
|
||||
<Card >
|
||||
<Card>
|
||||
<Statistic
|
||||
title="Current market size"
|
||||
value={totalMarketSize}
|
||||
precision={2}
|
||||
valueStyle={{ color: '#3f8600' }}
|
||||
valueStyle={{ color: "#3f8600" }}
|
||||
prefix="$"
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Card >
|
||||
<Card>
|
||||
<Statistic
|
||||
title="Total borrowed"
|
||||
value={totalBorrowed}
|
||||
|
@ -86,7 +88,6 @@ export const HomeView = () => {
|
|||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<div className="home-item home-header">
|
||||
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
||||
<div>{LABELS.TABLE_TITLE_MARKET_SIZE}</div>
|
||||
|
|
|
@ -12,19 +12,22 @@ export const LiquidateView = () => {
|
|||
{obligations.length === 0 ? (
|
||||
<div className="liquidate-info">{LABELS.LIQUIDATE_NO_LOANS}</div>
|
||||
) : (
|
||||
<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>
|
||||
{obligations.map((item) => (
|
||||
<LiquidateItem key={item.account.pubkey.toBase58()} item={item}></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>
|
||||
)}
|
||||
{obligations.map((item) => (
|
||||
<LiquidateItem
|
||||
key={item.account.pubkey.toBase58()}
|
||||
item={item}
|
||||
></LiquidateItem>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -13,19 +13,17 @@ import {
|
|||
} from "../../utils/utils";
|
||||
import { LABELS } from "../../constants";
|
||||
|
||||
export const LiquidateItem = (props: {
|
||||
item: EnrichedLendingObligation
|
||||
}) => {
|
||||
let obligation = props.item.info
|
||||
export const LiquidateItem = (props: { item: EnrichedLendingObligation }) => {
|
||||
let obligation = props.item.info;
|
||||
|
||||
const borrowReserve = cache.get(
|
||||
obligation.borrowReserve
|
||||
) as ParsedAccount<LendingReserve>;
|
||||
const borrowReserve = cache.get(obligation.borrowReserve) as ParsedAccount<
|
||||
LendingReserve
|
||||
>;
|
||||
|
||||
const collateralReserve = cache.get(
|
||||
obligation.collateralReserve
|
||||
) as ParsedAccount<LendingReserve>;
|
||||
|
||||
|
||||
const tokenName = useTokenName(borrowReserve?.info.liquidityMint);
|
||||
const liquidityMint = useMint(borrowReserve.info.liquidityMint);
|
||||
|
||||
|
@ -53,19 +51,13 @@ export const LiquidateItem = (props: {
|
|||
/>
|
||||
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
||||
</div>
|
||||
{collateralName}
|
||||
→
|
||||
{borrowName}
|
||||
{collateralName}→{borrowName}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(borrowAmount)} {tokenName}
|
||||
</div>
|
||||
<div>
|
||||
{formatPct.format(borrowAPY)}
|
||||
</div>
|
||||
<div>
|
||||
{formatPct.format(obligation.ltv / 100)}
|
||||
</div>
|
||||
<div>{formatPct.format(borrowAPY)}</div>
|
||||
<div>{formatPct.format(obligation.ltv / 100)}</div>
|
||||
<div>
|
||||
<Button>
|
||||
<span>{LABELS.LIQUIDATE_ACTION}</span>
|
||||
|
@ -75,4 +67,4 @@ export const LiquidateItem = (props: {
|
|||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -15,7 +15,9 @@ export const LiquidateReserveView = () => {
|
|||
|
||||
const obligation = useEnrichedLendingObligation(id);
|
||||
const reserve = useLendingReserve(obligation?.info.borrowReserve);
|
||||
const collateralReserve = useLendingReserve(obligation?.info.collateralReserve);
|
||||
const collateralReserve = useLendingReserve(
|
||||
obligation?.info.collateralReserve
|
||||
);
|
||||
|
||||
if (!obligation || !reserve) {
|
||||
return null;
|
||||
|
@ -37,5 +39,5 @@ export const LiquidateReserveView = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
@ -21,7 +21,6 @@ export const RepayReserveView = () => {
|
|||
obligationId ? lendingObligation?.info.borrowReserve : reserveId
|
||||
);
|
||||
|
||||
|
||||
const repayReserve = useLendingReserve(
|
||||
obligationId ? lendingObligation?.info.collateralReserve : reserveId
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue