Skip to content

Commit

Permalink
feat: Find price impact of swap
Browse files Browse the repository at this point in the history
  • Loading branch information
secretshardul committed Sep 26, 2021
1 parent 3f213aa commit 83e1f74
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 15 deletions.
53 changes: 38 additions & 15 deletions src/components/Info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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">
Expand All @@ -80,7 +104,7 @@ function InfoButton() {
PaperProps={{ style: { borderRadius: "10px" } }}
disableRestoreFocus
>
<InfoDetails />
<InfoDetails route={route} />
</Popover>
</div>
)
Expand All @@ -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;
Expand Down
30 changes: 30 additions & 0 deletions src/context/Dex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 83e1f74

Please sign in to comment.