diff --git a/src/components/Info.tsx b/src/components/Info.tsx
index f776c91b..8467e23a 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 3f7508e2..784ea35a 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);