From 83e1f744485a262cb2cb0644c7896c83465186ed Mon Sep 17 00:00:00 2001 From: secretshardul Date: Sun, 26 Sep 2021 17:33:00 +0530 Subject: [PATCH] feat: Find price impact of swap --- src/components/Info.tsx | 53 +++++++++++++++++++++++++++++------------ src/context/Dex.tsx | 30 +++++++++++++++++++++++ 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/src/components/Info.tsx b/src/components/Info.tsx index f776c91..8467e23 100644 --- a/src/components/Info.tsx +++ b/src/components/Info.tsx @@ -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 ( -
- - {fair !== undefined && toTokenInfo && fromTokenInfo - ? `1 ${toTokenInfo.symbol} = ${fair.toFixed( - fromMintInfo?.decimals - )} ${fromTokenInfo.symbol}` - : `-`} - - -
+ <> +
+ + {fair !== undefined && toTokenInfo && fromTokenInfo + ? `1 ${toTokenInfo.symbol} = ${fair.toFixed( + fromMintInfo?.decimals + )} ${fromTokenInfo.symbol}` + : `-`} + + +
+ +
+ + Price impact:  + + 10 ? "error" : "primary"} + > + {impact?.toFixed(2)}% + +
+ ); } -function InfoButton() { +function InfoButton({ route }: { route: PublicKey[] | null }) { const styles = useStyles(); return ( @@ -80,7 +104,7 @@ function InfoButton() { PaperProps={{ style: { borderRadius: "10px" } }} disableRestoreFocus > - + ) @@ -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; diff --git a/src/context/Dex.tsx b/src/context/Dex.tsx index 3f7508e..784ea35 100644 --- a/src/context/Dex.tsx +++ b/src/context/Dex.tsx @@ -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);