feat: Find price impact of swap

This commit is contained in:
secretshardul 2021-09-26 17:33:00 +05:30
parent 3f213aa5ae
commit 83e1f74448
2 changed files with 68 additions and 15 deletions

View File

@ -11,7 +11,12 @@ import { PublicKey } from "@solana/web3.js";
import { useTokenMap } from "../context/TokenList";
import { useSwapContext, useSwapFair } from "../context/Swap";
import { useMint } from "../context/Token";
import { useRoute, useMarketName, useBbo } from "../context/Dex";
import {
useRoute,
useMarketName,
useBbo,
usePriceImpact,
} from "../context/Dex";
const useStyles = makeStyles(() => ({
infoLabel: {
@ -39,21 +44,40 @@ export function InfoLabel() {
let fromTokenInfo = tokenMap.get(fromMint.toString());
let toTokenInfo = tokenMap.get(toMint.toString());
// Use last route item to find impact
const route = useRoute(fromMint, toMint);
const impact = usePriceImpact(route?.at(-1));
return (
<div className={styles.infoLabel}>
<Typography color="textSecondary" style={{ fontSize: "14px" }}>
{fair !== undefined && toTokenInfo && fromTokenInfo
? `1 ${toTokenInfo.symbol} = ${fair.toFixed(
fromMintInfo?.decimals
)} ${fromTokenInfo.symbol}`
: `-`}
</Typography>
<InfoButton />
</div>
<>
<div className={styles.infoLabel}>
<Typography color="textSecondary" style={{ fontSize: "14px" }}>
{fair !== undefined && toTokenInfo && fromTokenInfo
? `1 ${toTokenInfo.symbol} = ${fair.toFixed(
fromMintInfo?.decimals
)} ${fromTokenInfo.symbol}`
: `-`}
</Typography>
<InfoButton route={route} />
</div>
<div className={styles.infoLabel}>
<Typography color="textSecondary" style={{ fontSize: "14px" }}>
Price impact:&nbsp;
</Typography>
<Typography
style={{ fontSize: "14px", fontWeight: 500 }}
display="inline"
color={(impact ?? 0) > 10 ? "error" : "primary"}
>
{impact?.toFixed(2)}%
</Typography>
</div>
</>
);
}
function InfoButton() {
function InfoButton({ route }: { route: PublicKey[] | null }) {
const styles = useStyles();
return (
<PopupState variant="popover">
@ -80,7 +104,7 @@ function InfoButton() {
PaperProps={{ style: { borderRadius: "10px" } }}
disableRestoreFocus
>
<InfoDetails />
<InfoDetails route={route} />
</Popover>
</div>
)
@ -89,9 +113,8 @@ function InfoButton() {
);
}
function InfoDetails() {
function InfoDetails({ route }: { route: PublicKey[] | null }) {
const { fromMint, toMint } = useSwapContext();
const route = useRoute(fromMint, toMint);
const tokenMap = useTokenMap();
const fromMintTicker = tokenMap.get(fromMint.toString())?.symbol;
const toMintTicker = tokenMap.get(toMint.toString())?.symbol;

View File

@ -26,6 +26,7 @@ import {
import { useTokenMap, useTokenListContext } from "./TokenList";
import { fetchSolletInfo, requestWormholeSwapMarketIfNeeded } from "./Sollet";
import { setMintCache } from "./Token";
import { useSwapContext } from "./Swap";
const BASE_TAKER_FEE_BPS = 0.0022;
export const FEE_MULTIPLIER = 1 - BASE_TAKER_FEE_BPS;
@ -350,6 +351,35 @@ export function useMarketName(market: PublicKey): string | null {
return name;
}
// TODO handle edge case of insufficient liquidity
export function usePriceImpact(market?: PublicKey): number | undefined {
const { toAmount, toMint } = useSwapContext();
const orderbook = useOrderbook(market);
if (orderbook === undefined) {
return undefined;
}
const orders = toMint.equals(orderbook.bids.market.baseMintAddress)
? orderbook.asks.items(false)
: orderbook.bids.items(true);
let remainingAmount = toAmount;
let order = orders.next();
const initialPrice = order.value.price;
let priceAfterOrder = initialPrice;
while (!order.done && remainingAmount > 0) {
priceAfterOrder = order.value.price;
remainingAmount =
remainingAmount > order.value.size
? remainingAmount - order.value.size
: 0;
order = orders.next();
}
const priceChange = Math.abs(initialPrice - priceAfterOrder);
const impact = (priceChange * 100) / initialPrice;
return impact;
}
// Fair price for a given market, as defined by the mid.
export function useBbo(market?: PublicKey): Bbo | undefined {
const orderbook = useOrderbook(market);